文档教程【免费下载链接】typescript-bookThe Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.项目地址https://gitcode.com/gh_mirrors/typ/typescript-book点击查看免费下载判别式联合Discriminated Unions又称可辨识联合是 TypeScript 类型系统中最实用的模式之一它通过一个公共的判别属性discriminant让编译器能在运行时分支中精确收窄联合成员的类型。本文以开源项目The Concise TypeScript Book的 discriminated-unions 章节本仓库同时提供 英文原文为骨架结合仓库中 Narrowing、Control Flow Analysis、Exhaustiveness Checking 与 Never type 等相邻章节深入讲解判别式联合的定义、核心示例、底层收窄原理与穷尽性检查实践。读完本文你将能熟练用判别式联合建模同一事物的多种形态写出类型安全、可穷尽检查、易于维护的 TypeScript 代码。一、什么是判别式联合TypeScript 中的判别式联合是一种特殊的union type联合类型它使用一个公共属性——即判别属性discriminant——来收窄narrow down联合中可能的类型集合。先回顾普通联合类型正如 Union Type 章节 所讲联合类型表示一个值可以是若干类型之一用|符号连接每种可能的类型let x: string | number; x hello; // Valid x 123; // Valid普通联合类型在编译期虽然知道可能是 string 或 number但如果不做显式判断编译器并不知道当前x具体是哪一种。判别式联合正是在此基础上加上了可以精确区分成员的机制每个联合成员都是一个对象类型或 interface每个成员都包含同一个属性即判别属性该属性的类型必须是字面量类型如square、circle或枚举等可区分值每个成员的判别属性取值互不相同形成一一对应的指纹。这样编译器只需检查这个公共属性的值就能在控制流中确定对象具体属于哪个成员并自动收窄出该成员独有的属性。二、核心示例用kind判别 Square 与 Circle原文档给出了一个完整、可运行的经典示例——用判别式联合建模形状并用switch计算面积。下面逐段解读。定义判别式联合类型type Square { kind: square; // Discriminant size: number; }; type Circle { kind: circle; // Discriminant radius: number; }; type Shape Square | Circle;要点说明kind就是判别属性discriminant注释已明确标出Square.kind的字面量类型是squareCircle.kind是circle两者取值唯一且互斥Shape Square | Circle构成判别式联合kind是该联合的公共可判别键。用 switch 按判别属性收窄const area (shape: Shape) { switch (shape.kind) { case square: return Math.pow(shape.size, 2); case circle: return Math.PI * Math.pow(shape.radius, 2); } };这段代码的精髓在于当switch检查shape.kind square时TypeScript 的控制流分析会把shape收窄为Square因此在case square分支内可以安全访问shape.size同理在case circle分支内shape被收窄为Circle可以访问shape.radius。如果试图在某个分支访问不属于该成员类型的属性编译器会直接报错。实例化并验证输出const square: Square { kind: square, size: 5 }; const circle: Circle { kind: circle, radius: 2 }; console.log(area(square)); // 25 console.log(area(circle)); // 12.566370614359172运行结果验证area(square)Math.pow(5, 2) 25area(circle)Math.PI * Math.pow(2, 2)≈ 3.14159 × 4 ≈ 12.566370614359172。注意由于area的两个case分支都返回了值且Shape恰好只有两个成员TypeScript 能判定该函数在所有输入下都有返回值这同样是收窄分析的功劳。三、判别式联合如何与类型收窄协同工作判别式联合之所以开箱即用底层依赖 TypeScript 的类型收窄Narrowing能力。如 Narrowing 章节 所述narrowing 是在条件块内精化变量类型的过程在处理联合类型变量可能具有多种类型时非常有用。除了判别式联合依托的switch等值判断外TypeScript 还识别多种收窄方式它们可以按需组合typeof 类型守卫typeof type guards基于变量的内置 JavaScript 类型进行检查const fn (x: number | string) { if (typeof x number) { return x 1; // x is number } return -1; };真值收窄Truthiness narrowing通过变量是否为真值来收窄类型const toUpperCase (name: string | null) { if (name) { return name.toUpperCase(); } else { return null; } };等值收窄Equality narrowing通过与特定值比较来收窄类型常与switch及、!、、!等操作符配合使用——判别式联合正是这种方式的典型应用const checkStatus (status: success | error) { switch (status) { case success: return true; case error: return null; } };in 操作符收窄In operator narrowing基于属性是否存在来收窄类型适合判别属性为布尔/可选、难以用字面量区分的场景type Dog { name: string; breed: string; }; type Cat { name: string; likesCream: boolean; }; const getAnimalType (pet: Dog | Cat) { if (breed in pet) { return dog; } else { return cat; } };instanceof 收窄基于构造函数检查对象实例class Square { constructor(public width: number) {} } class Rectangle { constructor( public width: number, public height: number ) {} } function area(shape: Square | Rectangle) { if (shape instanceof Square) { return shape.width * shape.width; } else { return shape.width * shape.height; } } const square new Square(5); const rectangle new Rectangle(5, 10); console.log(area(square)); // 25 console.log(area(rectangle)); // 50判别式联合相对于in、instanceof等方案的优势在于判别属性是联合成员上显式声明、字面量可枚举的键编译器能据此做最精确、可穷尽的分析且在数据跨边界传递如 API 响应时无需运行时类型库即可安全判别。四、控制流分析判别式联合收窄的底层原理判别式联合的收窄由 TypeScript 的**控制流分析Control Flow Analysis**驱动。根据 Control Flow Analysis 章节控制流分析会静态分析代码执行流以推断变量类型并基于分析结果对变量类型进行必要收窄。该章节还指出了两个重要的能力边界直接关系到判别式联合的写法1. 通过 const 变量间接引用判别属性也可以收窄。在 TypeScript 4.4 之前代码流分析只作用于if语句内部从 TypeScript 4.4 起它也能作用于条件表达式以及通过 const 变量间接引用的判别属性访问。例如const f1 (x: unknown) { const isString typeof x string; if (isString) { x.length; } }; const f2 ( obj: { kind: foo; foo: string } | { kind: bar; bar: number } ) { const isFoo obj.kind foo; if (isFoo) { obj.foo; } else { obj.bar; } };在f2中把判别比较结果存入const isFoo后续if (isFoo)仍能正确收窄obj.kind对应的成员并访问obj.foo/obj.bar。这正是 4.4 版本带来的判别属性间接引用分析。2. 某些写法会破坏收窄。该章节也给出了收窄不发生的反例若比较结果存入非 const 变量或在函数体内对obj重新赋值收窄都会失效const f1 (x: unknown) { let isString typeof x string; if (isString) { x.length; // Error, no narrowing because isString it is not const } }; const f6 ( obj: { kind: foo; foo: string } | { kind: bar; bar: number } ) { const isFoo obj.kind foo; obj obj; if (isFoo) { obj.foo; // Error, no narrowing because obj is assigned in function body } };另外该章节注明条件表达式中最多分析五层间接引用up to five levels of indirection。因此在书写判别式联合时建议让判别比较尽量直接、扁平避免过度嵌套的间接引用。五、穷尽性检查用never兜住所有分支判别式联合的一大价值在于它天然支持穷尽性检查Exhaustiveness Checking。如 Exhaustiveness Checking 章节 所述穷尽性检查能确保判别式联合的所有可能情况都在switch或if中被处理。never 类型回顾never表示永远不会发生的值。根据 Never type 章节它常用于标注永不返回的函数或表达式例如无限循环与抛错const infiniteLoop (): never { while (true) { // do something } }; const throwError (message: string): never { throw new Error(message); };用 never 构建穷尽性检查在switch的default分支中将收窄后的变量赋给一个never类型变量。如果联合中出现了未被case处理的成员该成员的判别值类型不是never赋值就会产生编译错误——从而在编译期阻止新增类型却忘记处理的回归type Direction up | down; const move (direction: Direction) { switch (direction) { case up: console.log(Moving up); break; case down: console.log(Moving down); break; default: const exhaustiveCheck: never direction; console.log(exhaustiveCheck); // This line will never be executed } };never类型的用途就是确保 default 分支是穷尽的一旦有人往Direction增加新值例如left而没有在switch中处理TypeScript 就会在const exhaustiveCheck: never direction;处报错。更常见的实战写法是在 default 分支抛错让未穷尽在运行时也显式暴露type Direction up | down; const move (direction: Direction): void { switch (direction) { case up: // move up break; case down: // move down break; default: const exhaustiveCheck: never direction; throw new Error(Unhandled direction: ${exhaustiveCheck}); } };把这条实践与本文第二节的面积示例结合就得到判别式联合的完整最佳实践模板字面量判别属性 → switch 收窄各成员 → default 分支做 never 穷尽检查。六、实践要点与相关章节导航综合原文档与仓库相邻章节使用判别式联合时可遵循以下要点判别属性选型使用字符串/数字字面量类型或as const修饰的对象、枚举保证每个成员取值唯一且可枚举分支收窄优先用switch (x.discriminant)编译器会为每个case精确收窄成员类型间接引用若要先把判别比较结果存入变量务必使用const且注意最多五层间接引用的分析边界见 Control Flow Analysis穷尽兜底始终在default分支使用never赋值可配合抛错把遗漏分支变成编译错误适用场景API 响应判别如{ status: ok, data } | { status: error, message }、Redux/状态机 action 建模、UI 组件多形态渲染等。The Concise TypeScript Book 在 目录章节 中将判别式联合编排在探索类型系统 / 类型收窄一节的末尾紧随 Narrowing 与 Analisis Alur Kontrol其后紧跟 Tipe never 与 Pemeriksaan Kelengkapan。建议按联合类型 → 收窄 → 控制流分析 → 判别式联合 → never → 穷尽性检查的顺序连续阅读即可完整掌握这一模式的全部原理与工程实践。赞分享文档教程【免费下载链接】typescript-bookThe Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.项目地址https://gitcode.com/gh_mirrors/typ/typescript-book点击查看免费下载相关推荐The Concise TypeScript Book 精读Discriminated Unions可判别联合实战指南The Concise TypeScript Book 精读Discriminated Unions可判别联合实战指南 判别联合Discriminate文档教程The Concise TypeScript Book 精读Discriminated Unions 判别联合类型完全指南The Concise TypeScript Book 精读Discriminated Unions 判别联合类型完全指南 判别联合Discriminate文档教程The Concise TypeScript Book 精讲判别联合Discriminated Unions的类型收窄实战The Concise TypeScript Book 精讲判别联合Discriminated Unions的类型收窄实战 本篇为开源仓库 The Con文档教程上一篇7个实用技巧open-notebook故障注入测试实践指南提升系统可靠性的终极方法下一篇SWE-bench测试结果可视化直观展示评估数据创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
