是否可以从对象的联合中获取密钥?

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

TsPlayground

类似 keyof (A | B | C) 的结果只会导致 肯定 类型对象上的键 A | B | C,这意味着它必须是键已知在 ABCall 中,即: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。我只是说要小心。


好的,希望对您有所帮助;祝你好运!

Playground link to code

Failed 属于 never 类型,因为您的 Foo 类型不能有任何键。它目前设置为 3 种完全互斥类型之间的交集类型,因此没有有效键。

如果您从使用 | 更改为 &,那么它将按原样工作。

type Foo = { a: number } & { b: string } & { c: boolean }

type a = keyof Foo // 'a' | 'b' | 'c'

Playground Link