assertNever 穷尽检查
最后更新:2026-10-22
assertNever 穷尽检查
穷尽检查是保证"联合类型的每个成员都被处理"的编译期保险。核心是 assertNever 技巧:把 switch 的 default 分支参数声明为 never——如果漏了某个成员,那个成员还能传进来,参数就不是 never,编译器立刻报错。
| 分组 | 收窄与断言 |
|---|---|
| 类型 | 语法 |
| 严格模式 | 不需要 |
ℹ️ 说明 编辑器将 TypeScript 转译为 JavaScript 后运行(不做类型检查);类型检查结果见示例 Output。
📝 语法
function assertNever(x: never): never {
throw new Error("Unhandled: " + JSON.stringify(x));
}
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2;
case "square": return shape.side ** 2;
default:
return assertNever(shape); // 漏分支时这里编译报错
}
}
⚙️ 参数
| x: never | never 是所有类型的子类型且没有成员——只有当所有分支都处理完、剩余类型为空时,实参才满足 never |
|---|
返回值:assertNever 返回 never(永不正常返回,运行时抛错);编译期的"返回值"是错误提示:Argument of type 'X' is not assignable to parameter of type 'never'。
▶ 示例
修改下面的代码,点击试一试查看转译运行结果:
输出:
3.14
9
(转译后运行输出:当前分支齐全,assertNever 走不到。若给 Shape 新增 kind: "triangle" 而不加分支,tsc 会在 default 行报 "Argument of type '{ kind: "triangle"; ... }' is not assignable to parameter of type 'never'")
💡 要点
ℹ️ 说明 原理:TS 对联合的 switch 会把已处理成员从类型中剔除。所有成员都处理后 shape 就是 never,能赋给 never 参数;漏一个成员就赋不进去,报错位置正是 default 行。
💡 提示 给联合新增成员是穷尽检查最大的价值时刻:不用全局搜索调用点,编译器会把所有漏处理的 switch 一次列出来。
⚠️ 注意 穷尽检查依赖可辨识联合:判别字段必须是字面量类型。switch 上判别字段但用 if-else 链、或判别字段类型是 string,编译器都无法判断穷尽性。
🏷️ 相关收窄与断言
💡 相关案例
更多案例即将上线。
📚 相关教程
❓ 常见问题
Q 不用 assertNever,写全 switch 不行吗?
A TS 的 noImplicitReturns / strict 模式能发现"有的路径没有返回值",但无法发现"逻辑上少处理了一种情况"(比如某个分支 return 了默认值)。assertNever 把"漏处理"变成硬编译错误。
Q if-else 链也能做穷尽检查吗?
A 可以,在最后一个 else 里调用 assertNever(x) 即可,原理相同。但 switch 对可辨识联合的可读性和穷尽性分析都更友好,推荐 switch。
Q 运行时 assertNever 会被执行吗?
A 正确维护下不会——它只在"编译期已防住"的漏分支场景才会跑到。留着一个 throw 兜底是防御性编程:万一运行时数据与类型声明不符(如旧缓存数据),能尽早暴露而不是静默走错分支。