TypeScript 可辨识联合(Discriminated Unions):用类型消灭状态错误

可辨识联合用共同的"标签字段"让 TypeScript 自动收窄类型。本文讲解其原理、状态机建模、穷尽性检查(never 技巧),以及 API 响应与事件系统的实战应用。

最佳实践
类型结构与组件组合插画

可辨识联合 = 多个对象类型共享一个字面量"标签字段"(如 type: 'success' | 'error')。TypeScript 能根据标签自动收窄类型:switch 到 case 'success' 分支后,编辑器精确知道当前是 SuccessResponse——写错字段名当场报红,整类运行期错误在编译期消失。

问题:松散联合的坑

没有辨识标签时,联合类型的字段访问处处受限:

// 反面教材
interface Result {
  data?: User;
  error?: string;
}
// data 和 error 都可能 undefined,用的时候全是防御性判断

?. 满天飞、if (result.data) 判不准——"同时可能有值"和"同时可能没值"的非法状态都合法,bug 由此滋生。

解法:标签字段 + 联合

interface Success { status: 'success'; data: User }
interface Failure { status: 'error'; message: string }
interface Pending { status: 'pending' }

type Result = Success | Failure | Pending;

function render(r: Result) {
  switch (r.status) {
    case 'success':
      return r.data.name;      // TS 知道这里是 Success
    case 'error':
      return r.message;        // TS 知道这里是 Failure
    case 'pending':
      return '加载中…';
  }
}

status 就是辨识标签(discriminant):字面量类型 + 共有字段,TS 的控制流分析自动完成收窄。

穷尽性检查:让编译器帮你兜住遗漏

新增一种状态时,怎么保证所有 switch 都被更新?用 never 兜底:

function assertNever(x: never): never {
  throw new Error('未处理的分支: ' + JSON.stringify(x));
}

function render(r: Result) {
  switch (r.status) {
    case 'success': return r.data.name;
    case 'error':   return r.message;
    default:        return assertNever(r);  // 漏了 pending 时编译报错
  }
}

联合里新增成员后,所有没更新的 switch 全部编译报错——重构的安全性是类型系统白送的。

实战场景

  • 状态机:type State = Idle | Loading | Success | Failure,每个状态只携带自己需要的数据,消灭"loading 时 data 是什么"的糊涂账;
  • API 响应:type Response = Ok<T> | Err,配合泛型 interface Ok<T> { ok: true; value: T };
  • 事件系统:type Event = ClickEvent | ScrollEvent | KeyEvent,每种事件载荷不同,reducer 里 switch 精确收窄。

常见问题(FAQ)

Q:标签字段名有约定吗?

惯用 type、kind 或领域词(status)。整个项目统一一个名字最重要;跨库互操作时注意对齐(如 Redux 生态约定 type)。

Q:可辨识联合和枚举怎么分工?

枚举适合"一组固定值"(方向、级别);可辨识联合适合"值不同、携带的数据也不同"的变体。简单开关用枚举,结构化状态用联合——状态机几乎总是后者。

Q:嵌套的可辨识联合支持吗?

支持。外层收窄后内层继续收窄:if (r.status === 'success' && r.data.role === 'admin') 逐层精确。深层结构建议配合拆分小的类型守卫函数保持可读。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台