为什么keyof never是 string、number和 symbol的联合类型?

前端开发 2026-07-09

当我对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 neverkeyof 的规则保持一致。由于 keyof TT 中是逆变的,并且 never 完全没有任何值,keyof never 应该包含所有值。因此 keyof never 是所有可能键类型的并集,也就是说 string | number | symbol

更详细的解释:


keyof 运算符 在其参数上天然具有逆变 的特性,意味着 T extends U 会导致 keyof U extends keyof T,反之亦然。请注意 TU 的位置会互换;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。关于这方面的规则,请参阅 映射类型中对 numbersymbol 键的支持

由于 keyof 是逆变的,且因为 never 是你能得到的最窄的类型,keyof never 应该是你能得到的最宽的键类型。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章