You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何根据判别式值推断联合类型的具体子类型

TypeScript 标记联合类型匹配工具的子类型提取方案

问题背景

在TypeScript项目中使用标记联合类型(tagged unions,又称代数数据类型)时,常规的switch是语句而非表达式,处理联合类型值时必须用立即执行函数包裹才能拿到返回值:

// 示例标记联合类型定义
type CheckNumber = string;
type CreditCardType = 'visa' | 'mastercard';
type CreditCardNumber = string;
type PaymentMethod =
  | { method: "cash" }
  | { method: "check", checkNumber: CheckNumber }
  | { method: "credit", cardType: CreditCardType, cardNumber: CreditCardNumber }

const payment: PaymentMethod = { method: 'cash' };
// 必须包IIFE才能拿到switch的返回值
const description = (() => {
  switch (payment.method) {
    case 'cash': return `Paid with cash`;
    case 'check': return `Paid with check nr. ${payment.checkNumber}`;
    case 'credit': return `Paid with a ${payment.cardType} credit card, nr. ${payment.cardNumber}`;
  }
})();

这种写法虽然可以借助TS的类型推断实现标签值的穷尽性检查,但语法冗余繁琐。

预期目标

实现一个通用匹配工具函数,用更简洁的表达式写法完成同样的类型安全匹配:

// 基于判别字段生成匹配函数
const matchPaymentMethod = matchingWith<PaymentMethod, 'method'>('method');

const payment: PaymentMethod = { method: 'credit', cardType: 'visa', cardNumber: '4111111111111111' };
// 表达式风格调用,每个分支参数自动推导为对应子类型
const description = matchPaymentMethod(payment, {
  cash: cash => `Paid with cash`,
  check: check => `Paid with check nr. ${check.checkNumber}`,
  credit: credit => `Paid with a ${credit.cardType} credit card, nr. ${credit.cardNumber}`,
});

实现过程中的核心卡点是:如何在类型层面提取出联合类型中,指定标签值对应的子类型,也就是实现类似T where T[K] = C的类型筛选能力。

实现方案

TypeScript内置的Extract工具类型完全可以满足这个筛选需求,不需要自定义复杂类型逻辑。

完整类型定义与实现

type MatchingWith = <T, K extends keyof T>(tagKey: K) => 
  <R>(
    value: T, 
    matching: {
      // 遍历联合类型所有可能的标签值
      [C in T[K]]: (matched: Extract<T, Record<K, C>>) => R
    }
  ) => R;

const matchingWith: MatchingWith = (tagKey) => (value, matching) => {
  const tag = value[tagKey];
  return matching[tag as keyof typeof matching](value as never);
}

关键逻辑说明

  • Extract<T, U>是TS内置的条件类型,作用是从联合类型T中筛选出可赋值给U的成员,正好对应T where T extends U的语义
  • 这里用Record<K, C>构造“标签键K对应值为C”的对象结构,传给Extract就能精准筛选出对应标签的子类型
  • 类型定义会自动遍历T[K]的所有字面量类型作为匹配对象的键,自动实现穷尽性检查,漏写分支时TS会直接报类型错误
  • 运行时逻辑仅做属性读取和函数调用,没有额外开销

类型效果验证

使用上述实现后,每个匹配分支的回调参数会自动推导为对应标签的子类型:

  • cash分支参数自动推导为{ method: 'cash' }
  • check分支参数自动推导为{ method: 'check', checkNumber: CheckNumber }
  • credit分支参数自动推导为{ method: 'credit', cardType: CreditCardType, cardNumber: CreditCardNumber }
    完全保留原生switch的类型安全能力,同时写法更简洁,符合函数式表达式的使用习惯。

内容的提问来源于stack exchange,提问作者Alexander D.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 12:21:33