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

如何在嵌套变体场景下使用TypeScript的Extract<T, U>?

TypeScript从单对象联合type字段提取特定变体的解决方案

问题场景

我在处理API返回类型时遇到问题:API会返回两种类型之一,我希望分别为它们创建类型别名。TypeScript的Extract<>工具类型在处理联合类型时可以正常工作,示例如下:

type Add = {
    type: 'add';
    id: string;
}  

type Remove = {
    type: 'remove';
    id: string;
}  

type Action = Add | Remove;

type addType = Extract<Action, {type: 'add'}>;
// ^? 类型为Add

但我的类型定义是把两种变体合并到单个对象中,type字段是联合字面量,此时用Extract<>会返回never:

type ActionsCombined = {
    type: 'add' | 'remove';
    id: string;
}

type addTypeCombined = Extract<ActionsCombined, {type: 'add'}>;
// ^? 类型为never

看起来两种形式逻辑等价,但TypeScript不认可。想知道如何从ActionsCombined中提取add变体,或者将这种形式转换为独立的联合变体。

为什么两种形式不等价

第一种是区分联合类型(discriminated union),由两个独立的对象类型组成联合,每个对象的type是单一字面量;第二种是单个对象类型,仅type字段是联合字面量。Extract<T, U>的作用是从联合类型T中筛选出可赋值给U的成员,而第二种形式的ActionsCombined不是联合类型,因此无法匹配,返回never。

解决方案

方案1:先拆分联合类型

通过分布式条件类型将ActionsCombined拆分为真正的区分联合类型,之后就可以正常使用Extract<>:

// 拆分工具类型:将type为联合的对象拆分为多个对象的联合
type SplitUnion<T extends { type: string }> = T['type'] extends infer U 
  ? U extends string 
    ? { type: U } & Omit<T, 'type'> 
    : never 
  : never;

// 拆分后的联合类型
type ActionSplit = SplitUnion<ActionsCombined>;
// ^? { type: 'add'; id: string; } | { type: 'remove'; id: string; }

// 提取add变体
type addTypeCombined = Extract<ActionSplit, { type: 'add' }>;
// ^? { type: 'add'; id: string; }

方案2:直接提取特定变体

无需拆分,直接用条件类型提取目标type对应的类型:

// 提取工具类型:从type为联合的对象中筛选指定type的变体
type ExtractAction<T, K extends T['type']> = T extends { type: K } ? T : never;

// 提取add变体
type addTypeCombined = ExtractAction<ActionsCombined, 'add'>;
// ^? { type: 'add'; id: string; }

这里利用了TypeScript的分布式条件类型特性:当泛型参数关联的字段是联合类型时,条件会自动遍历每个联合成员,返回符合条件的结果集合。

内容的提问来源于stack exchange,提问作者Slava Knyazev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 11:52:50