如何在嵌套变体场景下使用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
相关产品推荐
相关产品推荐

