【发布时间】:2020-08-24 08:46:05
【问题描述】:
最简单的例子
假设这种类型
type Foo = { a: number } | { b: string } | { c: boolean };
有没有可能获得
type KeysOfFoo = 'a' | 'b' | 'c';
我试过了,但是不行
type Failed = keyof Foo; // never
【问题讨论】:
最简单的例子
假设这种类型
type Foo = { a: number } | { b: string } | { c: boolean };
有没有可能获得
type KeysOfFoo = 'a' | 'b' | 'c';
我试过了,但是不行
type Failed = keyof Foo; // never
【问题讨论】:
Failed 是 never 类型,因为您的 Foo 类型不能有任何键。它目前设置为 3 种完全互斥类型之间的交集类型,因此没有有效的键。
如果您从使用 | 更改为 &,那么它将照常工作。
type Foo = { a: number } & { b: string } & { c: boolean }
type a = keyof Foo // 'a' | 'b' | 'c'
【讨论】:
| 是一个并集,而不是一个交集。并且这三种类型不是独有的;他们只是不共享任何声明的属性。 {a: number} 和 {a: string} 将是独占的,因为您无法拥有同时满足这两种类型的对象。
像keyof (A | B | C) 这样的东西只会导致A | B | C 类型的对象上绝对 的键,这意味着它必须是已知在all 的 A、B 和 C,即:keyof A & keyof B & keyof C。也就是说,keyof T 是“contravariant in T”。
不过,这不是您想要的(在您的情况下,没有共同的键,所以交叉点是 never)。
如果您正在寻找您的工会成员中至少一个 中的一组键,您需要分发 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。我只是说要小心。
好的,希望对您有所帮助;祝你好运!
【讨论】: