microsoft / microsoft/TypeScript
Request: keyword or operator for iterating unions
まだ誰も着手していません。
- 主要言語
- 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 onT.
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.
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
まず、issue の conditional-type workaround、union に対する keyof の動作、およびリンクされている TypeScript の設計目標を確認してください。提案されている in、union、memberof の構文案を、それらの動機となっている例と比較してください。完了とするには、合意された言語設計と実装範囲が必要ですが、この issue ではまだ指定されていません。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- typescript
- 領域
- compilers
- issue の種類
- 機能追加
- 難易度
- 5/5
- 見積もり時間
- 1週間以上
- 活発さ
- 停滞
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 25/100