microsoft / microsoft/TypeScript

Request: keyword or operator for iterating unions

オープン
#43,694 コメント 4 件 リアクション 6 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

Awaiting More Feedback Suggestion
主要言語
Go
スター
111k
フォーク
14.3k
平均マージ
2日 4時間
マージ済み PR(30日)
132

説明

Suggestion

I'm sorry, I feel like this has definitely been suggested but I couldn't find it in issues.

I think there should be a more explicit way to force iterating through types of a union in generics than what we currently have. The easiest example I can think of is the way keyof interacts with unions.

interface A {
  foo: string;
}

interface B {
  bar: number;
}

type C = A | B;

type Keys = keyof C; // never

I understand why it works this way. I'm not arguing that TypeScript is doing anything wrong. However, sometimes I want to get all the keys of all the members of a union, and the workaround is to abuse conditional types to force iteration.

type KeyofUnion<T> = T extends T ? keyof T : never;

type Keys = KeyofUnion<A | B>; // 'foo' | 'bar';

The expression T extends T ? /* something */ : never is silly enough that I feel it justifies creating a keyword. More importantly, it's not clear that types would behave this way if you didn't look it up. Why would conditional types force union iteration? I assume there is a good reason, but for someone not super familiar with TS, I would never expect them to see that and understand why anyone wrote it.

I believe this suggestion would be an improvement under #7 of the "non-goals" of the contributing guidelines.

🔍 Search Terms

Iterating unions, distributing unions, T extends T ? T : never

✅ Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Add a keyword that explicitly iterates types in a union instead of relying on conditional types.

type A {
  foo: string;
}
type B {
  bar: number;
}
type C = A | B;

type Keys = keyof in C;
type Keys = keyof union C;
type Keys = keyof memberof C;

I'm not happy with any of the ideas I have for the syntax but hopefully you can see what I'm going for.

📃 Motivating Example

I already put one example in the first section, but I'll also give a more concrete example of something I was trying to do that made me think of this.

I wanted to create a generic type that takes in two args.

  • T, an object
  • and F, a type that exists as a member on T.

The resulting type is a union of all keys in T whose members are type F.

Example

interface Foo {
  a: string;
  b: number;
  c: Date;
  d: string;
  e: string;
  f: number;
}

type NumberKeys = KeysOfType<Foo, number>; // 'b' | 'f';
type StringKeys = KeysOfType<Foo, string>; // 'a' | 'd' | 'e';
type BoolKeys = KeysOfType<Foo, boolean>; // Error: Type 'boolean' does not satisfy the constraint 'string | number | Date'.

This was my first version of this type

type KeysOfType<T, F extends T[keyof T]> = {
  [K in keyof T]: T[K] extends F ? K : never;
}[keyof T];

But, when I tried to pass a union in as the first argument, it didn't work the way I wanted it to. This led me to change it to this:

type KeysOfType<T, F extends T extends T ? T[keyof T] : never> = T extends T
  ? {
      [K in keyof T]: T[K] extends F ? K : never;
    }[keyof T]
  : never;

This is much more cumbersome.

I'd love to see a way to force this without abusing conditional types.

type KeysOfType<T, F extends T[keyof in T]> = {
 [K in keyof in T]: T[K] extends F ? K : never;
}[keyof T];

type KeysOfType<T, F extends T[keyof union T]> = {
 [K in keyof union T]: T[K] extends F ? K : never;
}[keyof T];

Again, I really don't have any good ideas for the syntax, but hopefully the spirit of this request is clear at this point.

💻 Use Cases

I'm having a hard time thinking of another use case than the one I provided here, so maybe it's edge enough to be too low priority. I still think it'd be nice to have an explicit way to do this.

Thanks for reading this and have a good day.

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず、issue の conditional-type workaround、union に対する keyof の動作、およびリンクされている TypeScript の設計目標を確認してください。提案されている inunionmemberof の構文案を、それらの動機となっている例と比較してください。完了とするには、合意された言語設計と実装範囲が必要ですが、この issue ではまだ指定されていません。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
typescript
領域
compilers
issue の種類
機能追加
難易度
5/5
見積もり時間
1週間以上
活発さ
停滞
明瞭さ
おおむね明確
初心者へのやさしさ
25/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。