为什么keyof never是 string、number和 symbol的联合类型?
当我对never类型使用keyof运算符时,它会解析为所有可能属性键的并集,类似于keyof any。
type t = keyof never // same as keyof any. which is string | number | Symbol
将鼠标悬停在t 上时显示
string | number | Symbol
解决方案
这在 microsoft/TypeScript#33025 中有所讨论。简短的答案是这是有意为之的行为,目的是使 keyof never 与 keyof 的规则保持一致。由于 keyof T 在 T 中是逆变的,并且 never 完全没有任何值,keyof never 应该包含所有值。因此 keyof never 是所有可能键类型的并集,也就是说 string | number | symbol。
更详细的解释:
keyof 运算符 在其参数上天然具有逆变 的特性,意味着 T extends U 会导致 keyof U extends keyof T,反之亦然。请注意 T 与 U 的位置会互换;keyof X 的变化方向与 X 相反。它们“反向变动”,因此被称为“逆变”。
逆变会把 联合类型 转换成 交叉类型,反之亦然。因此一般规则是:
keyof (T & U) = keyof T | keyof U:像{a: string} & {b: number}这样的对象类型的交集,看起来就像一个拥有所有属性的单一对象类型,例如{a: string; b: number}。因此,交集的键自然是各成员键的并集。keyof (T | U) = keyof T & keyof U:像{a: string, c: string} | {b: number, c: number}这样的对象类型的并集,表现有点像一个只有公共属性的单一对象类型,例如{c: string | number}。在你弄清一个对象对应于并集的哪一个成员之前,唯一安全可检查的属性就是在所有并集成员中都存在的属性。(有关更多信息,请参阅 突出显示的手册文本)。因此,并集的键自然会是各成员键的交集。
因此无论 keyof never 是什么,它都应遵循这些规则。
The never type 是TypeScript的底部 类型,意思是它是最窄的可能类型。它是每种类型的交集。没有这个类型的值;它是不可居住的;你将“永远”看不到这个类型的值。对于任意类型 T:
T | never只是T(意味着never会被并入并集);以及T & never只是never(意味着never在交集中吸收其他成员)。
综合前面的规则可以推出
keyof (T | never) = keyof T = keyof T & keyof never。因此keyof never需要是一个能被所有交集吸收的类型。那就必须是键的 顶类型,因此它必须是任意可能的键,例如string | number | symbol。(它也可能是unknown类型,它是TypeScript的顶层类型,但对于键来说,这比我们需要的要宽。)keyof (T & never) = keyof never = keyof T | keyof never。因此keyof never需要是一个能吸收所有并集的类型。再次说明,这意味着它必须是键的顶类型。
这两个规则给出了一个要求(或至少强烈的暗示):keyof never 应该是它能达到的最宽的类型,对于键而言这就是 string | number | symbol。关于这方面的规则,请参阅 映射类型中对 number 与 symbol 键的支持。
由于 keyof 是逆变的,且因为 never 是你能得到的最窄的类型,keyof never 应该是你能得到的最宽的键类型。