编程TypeScript 架构师

如何通过 TypeScript 中的判别联合实现模式匹配?如何正确构造类型以及可能有哪些陷阱?

用 Hintsage AI 助手通过面试

回答

在 TypeScript 中,模式匹配通过 "判别联合"(discriminated unions)实现。每个联合中的对象都被分配一个必选的判别字段(通常是字符串,例如 type),TypeScript 根据该字段区分不同的变体。

示例:

type Success = { type: 'success'; data: string }; type Failure = { type: 'failure'; error: string }; type Result = Success | Failure; function handleResult(result: Result) { switch (result.type) { case 'success': // result: Success console.log(result.data); break; case 'failure': // result: Failure console.error(result.error); break; } }

在 switch/case 或者 if 中,根据判别字段,TypeScript 会“缩小”类型到所需的变体。

主要优点

  • 严格的类型检查 — 无法访问不存在的字段。
  • 完整性检查 — 如果没有处理所有变体,有时会触发错误(可以显式强制执行)。

有陷阱的问题

如果在判别联合中添加新的变体,TypeScript 是否会强制要求更新所有的 switch-case ,以处理新的变体?

回答: 不,只有在显式添加对“不可用”变体的处理时才会。

示例:

function assertNever(x: never): never { throw new Error('Unexpected variant: ' + x); } function handle(r: Result) { switch(r.type) { case 'success': /* ... */; break; case 'failure': /* ... */; break; default: return assertNever(r); // 如果出现新类型,TS 将会报错 } }

由于对主题细节的不理解而导致的真实错误实例。


故事

在将类型 "Result" 扩展为新的变体('pending')后,应用程序中的多个地方旧的 switch-case 没有处理这个案例。因此,部分接口停止工作。这个错误在发布后一周才在生产环境中被发现。


故事

尝试使用没有唯一判别者的判别联合(type 字段在两个类型中重复)导致了类型的“模糊化”:TypeScript 不再能准确缩小类型,允许在没有编译错误的情况下访问不存在的字段。几个关键的 bug 被发布到生产环境中。


故事

在项目中通过多个字段的 if-else 实现模式匹配,而不是使用一个明确的判别者。这使得过渡到带有 never 函数的完整性检查变得复杂,并且降低了代码的可读性 — switch-case 工作不正常,新的变体“破坏”了现有逻辑。