IsNever
大约 3 分钟
IsNever
题目
实现类型 IsNever,判断传入的类型 T 是否为 never。
如果是 never,返回 true;否则返回 false。
type A = IsNever<never> // 期望为 true
type B = IsNever<undefined> // 期望为 false
type C = IsNever<null> // 期望为 false
type D = IsNever<[]> // 期望为 false
type E = IsNever<number> // 期望为 false解法
type IsNever<T> = [T] extends [never] ? true : false一行代码。看起来简单,玄机全在那对方括号里。
深入理解
为什么不能直接写 T extends never?
// ❌ 错误写法
type IsNeverWrong<T> = T extends never ? true : false
type Test = IsNeverWrong<never> // never,不是 true!结果是 never 而不是 true。发生了什么?
又是分布式条件类型的锅
当你写 T extends Constraint ? A : B,并且 T 是一个裸类型参数时,TypeScript 会对联合类型进行特殊处理——它会把条件分布到联合的每个成员上:
type Distributed = (string | number) extends string ? 'yes' : 'no'
// 展开为:
// (string extends string ? 'yes' : 'no') | (number extends string ? 'yes' : 'no')
// = 'yes' | 'no'问题来了:never 是空联合——一个没有任何成员的联合。当你对空集分布时,什么都得不到:
// T = never(空联合)
// 分布:... 遍历零个成员 ...
// 结果:never(零个结果的联合)条件根本没被求值。TypeScript 直接短路返回 never。
元组包裹技巧
用元组 [T] 把 T 包起来可以禁用分布:
type IsNever<T> = [T] extends [never] ? true : false现在 [T] 不再是裸类型参数——它是一个包含 T 的元组类型。TypeScript 不会对元组类型分布,所以条件只会被求值一次:
- 当
T = never:[never] extends [never]→true✓ - 当
T = string:[string] extends [never]→false✓
为什么 [never] 能用但 never 不行?
| 表达式 | 行为 |
|---|---|
never extends never | 对空集分布 → never |
[never] extends [never] | 单次比较 → true |
元组充当容器,把类型"固化"住,阻止了空联合的特殊行为。
使用这个技巧的常见模式
// 检测 never
type IsNever<T> = [T] extends [never] ? true : false
// 阻止任何联合的分布
type NoDistribute<T> = [T] extends [T] ? T : never
// 无分布的安全比较
type Equals<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false相关陷阱:any
注意 [any] extends [never] 的结果是 boolean,不是 true 或 false:
type Test = [any] extends [never] ? true : false // boolean因为 any 同时可赋值和不可赋值给任何类型——TypeScript 两边都不敢押,把两个分支都返回为联合。如果你需要一个同时拒绝 any 的 IsNever,需要额外检测:
type IsStrictNever<T> = [T] extends [never]
? (0 extends (1 & T) ? false : true) // 排除 any
: false但大多数场景下,简单的 [T] extends [never] 就够用了。
核心要点
never是空联合——对它分布会得到never,不是布尔值- 元组包裹
[T]禁用分布行为,让比较正常进行 [T] extends [never]是 TypeScript 中检测never的标准写法- 小心
any——它让extends检测返回boolean(两个分支都有) - 这个模式无处不在:
IsNever、IsAny、IsUnion、Equals等都用到它
