是否可以从对象的联合中获取密钥?
Is it possible to get the keys from a union of objects?
最简单的例子
假设这种类型
type Foo = { a: number } | { b: string } | { c: boolean };
有没有可能得到
type KeysOfFoo = 'a' | 'b' | 'c';
我试过了,但没用
type Failed = keyof Foo; // never
类似 keyof (A | B | C)
的结果只会导致 肯定 类型对象上的键 A | B | C
,这意味着它必须是键已知在 A
、B
和 C
的 all 中,即:keyof A & keyof B & keyof C
。也就是说,keyof T
是“T
中的contravariant”。
这不是你想要的(在你的情况下没有共同的键所以交集是 never
)。
如果您要查找 至少一个 联盟成员中的密钥集,则需要 distribute 联合成员上的 keyof
运算符。幸运的是,有一种方法可以通过 distributive conditional types 做到这一点。它看起来像这样:
type AllKeys<T> = T extends any ? keyof T : never;
T extends any
在类型检查方面做的不多,但它确实向编译器发出信号,表明对 T
的操作应该分别针对 T
的每个联合成员进行然后结果将重新组合成一个联盟。这意味着 AllKeys<A | B | C>
将被视为 AllKeys<A> | AllKeys<B> | AllKeys<C>
。让我们试试看:
type KeysOfFoo = AllKeys<Foo>;
// type KeysOfFoo = "a" | "b" | "c"
看起来不错!请注意,在实际使用 KeysOfFoo
与类型 Foo
的对象时,您应该小心。 keyof
是逆变的,原因如下:
function hmm(foo: Foo, k: AllKeys<Foo>) {
foo[k]; // error!
// "a" | "b" | "c"' can't be used to index type 'Foo'.
// Property 'a' does not exist on type 'Foo'
}
使用 k
索引到 foo
是不安全的,原因与使用 "b"
不能安全地索引到类型 {a: number}
的值相同。 . 对象上可能不存在密钥。显然你比我更了解你的用例,所以你可能会合法地一起使用 AllKeys<Foo>
和 Foo
。我只是说要小心。
好的,希望对您有所帮助;祝你好运!
Failed
属于 never
类型,因为您的 Foo
类型不能有任何键。它目前设置为 3 种完全互斥类型之间的交集类型,因此没有有效键。
如果您从使用 |
更改为 &
,那么它将按原样工作。
type Foo = { a: number } & { b: string } & { c: boolean }
type a = keyof Foo // 'a' | 'b' | 'c'
最简单的例子
假设这种类型
type Foo = { a: number } | { b: string } | { c: boolean };
有没有可能得到
type KeysOfFoo = 'a' | 'b' | 'c';
我试过了,但没用
type Failed = keyof Foo; // never
类似 keyof (A | B | C)
的结果只会导致 肯定 类型对象上的键 A | B | C
,这意味着它必须是键已知在 A
、B
和 C
的 all 中,即:keyof A & keyof B & keyof C
。也就是说,keyof T
是“T
中的contravariant”。
这不是你想要的(在你的情况下没有共同的键所以交集是 never
)。
如果您要查找 至少一个 联盟成员中的密钥集,则需要 distribute 联合成员上的 keyof
运算符。幸运的是,有一种方法可以通过 distributive conditional types 做到这一点。它看起来像这样:
type AllKeys<T> = T extends any ? keyof T : never;
T extends any
在类型检查方面做的不多,但它确实向编译器发出信号,表明对 T
的操作应该分别针对 T
的每个联合成员进行然后结果将重新组合成一个联盟。这意味着 AllKeys<A | B | C>
将被视为 AllKeys<A> | AllKeys<B> | AllKeys<C>
。让我们试试看:
type KeysOfFoo = AllKeys<Foo>;
// type KeysOfFoo = "a" | "b" | "c"
看起来不错!请注意,在实际使用 KeysOfFoo
与类型 Foo
的对象时,您应该小心。 keyof
是逆变的,原因如下:
function hmm(foo: Foo, k: AllKeys<Foo>) {
foo[k]; // error!
// "a" | "b" | "c"' can't be used to index type 'Foo'.
// Property 'a' does not exist on type 'Foo'
}
使用 k
索引到 foo
是不安全的,原因与使用 "b"
不能安全地索引到类型 {a: number}
的值相同。 . 对象上可能不存在密钥。显然你比我更了解你的用例,所以你可能会合法地一起使用 AllKeys<Foo>
和 Foo
。我只是说要小心。
好的,希望对您有所帮助;祝你好运!
Failed
属于 never
类型,因为您的 Foo
类型不能有任何键。它目前设置为 3 种完全互斥类型之间的交集类型,因此没有有效键。
如果您从使用 |
更改为 &
,那么它将按原样工作。
type Foo = { a: number } & { b: string } & { c: boolean }
type a = keyof Foo // 'a' | 'b' | 'c'