使用 never 进行穷尽性检查
使用 never 在编译时捕获未处理的情况。
使用 never 进行穷尽性检查 是 CoddyKit 上的免费 TypeScript Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 TypeScript Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 TypeScript Academy 课程共包含 4 节课。
穷尽性问题
添加新的联合成员后,很容易忘记在某个位置处理它。穷尽性检查会将这种疏漏转化为编译错误。
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
// If we add "triangle" later, we want every switch to complain.永不存在类型
never 类型表示永远不会出现的值。如果所有分支都已处理,到达默认分支的值类型就是 never。
function fail(): never {
throw new Error("unreachable");
}
// never is assignable to nothing except never itself.在默认分支中赋值给永不存在类型
在 default 分支中,将该值赋给一个 never 变量。如果所有变体都已处理,赋值就能通过编译;否则会产生错误。
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default:
const _exhaustive: never = s;
return _exhaustive;
}
}
console.log(area({ kind: "square", side: 3 }));忘记某个分支时会发生什么
如果添加了 triangle 成员却忘记添加对应分支,那么默认分支中的 s 就不再是 never,因此赋值会在编译时失败。
// type Shape = ... | { kind: "triangle"; base: number; height: number };
// Now in default, s is { kind: "triangle"; ... }
// const _exhaustive: never = s; // Error: triangle not assignable to neverassertNever 辅助函数
可复用的 assertNever 函数可以集中实现这种模式。它接收 never 并抛出错误,同时说明该分支本应无法到达。
function assertNever(value: never): never {
throw new Error("Unhandled case: " + JSON.stringify(value));
}
console.log(typeof assertNever);在分支选择中使用 assertNever
在默认分支中调用 assertNever(s)。它会在编译时强制执行穷尽性检查,并在运行时意外到达该分支时提供清晰的错误。
function assertNever(value: never): never {
throw new Error("Unhandled: " + JSON.stringify(value));
}
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default: return assertNever(s);
}
}
console.log(area({ kind: "circle", radius: 1 }).toFixed(2));编译时安全与运行时安全
never 检查会在运行代码之前捕获遗漏的分支;如果某些情况绕过了类型检查,抛出的错误还可以在运行时提供保护。
function assertNever(x: never): never {
throw new Error("Unhandled: " + String(x));
}
// Compile error if a case is missing; runtime throw as a backstop.
console.log("two layers of safety");不使用默认分支进行穷尽性检查
如果函数具有显式返回类型,并且分支选择语句覆盖了每个分支,TypeScript 还可以标记缺少返回值的情况,这也是一种穷尽性检查。
type Light = "red" | "yellow" | "green";
function next(l: Light): Light {
switch (l) {
case "red": return "green";
case "yellow": return "red";
case "green": return "yellow";
}
// No default needed; all cases return.
}
console.log(next("red"));使用条件分支链进行穷尽性检查
同样的思路也适用于 if/else。处理完每个变体后,最后的 else 会接收到一个 never 值。
function assertNever(x: never): never { throw new Error("bad"); }
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function name(s: Shape): string {
if (s.kind === "circle") return "circle";
else if (s.kind === "square") return "square";
else return assertNever(s);
}
console.log(name({ kind: "square", side: 2 }));为什么永不存在类型是正确工具
由于 never 不能赋值给任何其他类型,任何遗留的变体都会导致赋值失败。因此,never 是检测未处理分支的理想工具。
// Only never is assignable to never.
let x: never;
// x = "hi"; // Error
// Any concrete leftover type fails the same way.
console.log("never catches gaps");将穷尽性检查作为重构安全网
在各处使用 assertNever 后,添加联合成员会产生一份整洁的编译错误列表,准确指出每个需要更新的位置。
function assertNever(x: never): never { throw new Error("unhandled"); }
type Status = "idle" | "busy";
function render(s: Status): string {
switch (s) {
case "idle": return "Idle";
case "busy": return "Busy";
default: return assertNever(s);
}
}
console.log(render("idle"));快速检查:穷尽性
测试您对穷尽性检查的理解。
回顾:使用永不存在类型进行穷尽性检查
您已经学会将默认值赋给 never(或将它传递给 assertNever),从而让编译器强制您处理每个变体。这样可以将遗漏的分支转化为编译错误。
function assertNever(x: never): never { throw new Error("unhandled"); }
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function f(s: Shape) {
switch (s.kind) {
case "circle": return s.radius;
case "square": return s.side;
default: return assertNever(s);
}
}
console.log(f({ kind: "circle", radius: 5 }));常见问题解答
「使用 never 进行穷尽性检查」课时是免费的吗?
是的 — 「使用 never 进行穷尽性检查」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 TypeScript Academy 课程的其余内容,请升级到 CoddyKit PRO。 TypeScript Academy 课程共包含 4 节课。
「使用 never 进行穷尽性检查」这节课中我会学到什么?
使用 never 在编译时捕获未处理的情况。 你通过在浏览器中直接运行的动手代码来练习 TypeScript Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 TypeScript Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 TypeScript Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「使用 never 进行穷尽性检查」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 TypeScript Academy 课中编写并运行代码吗?
能。每节 TypeScript Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- 构建可辨识联合
- 根据辨识属性进行收窄
- 使用 never 进行穷尽性检查
- 状态机建模