联合类型分发是TypeScript中对裸类型参数自动拆解联合成员并分别判断的机制;仅当T直接出现在条件类型中(如T extends string)才触发,包裹后(如[T])则整体判断,结合infer可精准过滤或解构联合类型。

联合类型分发(Distributive Conditional Types)不是语法糖,而是 TypeScript 类型系统中一种自动触发的行为——只要条件类型作用在裸类型参数(naked type parameter)上,且该参数被实例化为联合类型(如 A | B | C),TS 就会把它拆开,对每个成员单独执行判断,再把结果合并成新联合类型。掌握这个机制,是写出可复用、可组合、可预测的类型工具的关键。
理解“裸类型参数”是前提
所谓“裸”,指泛型参数 T 在条件类型中直接出现,未被包裹(比如没加 []、{}、Promise 等)。只有裸参数才会触发分发:
-
✅ 触发分发:
type Foo——= T extends string ? number : boolean; T是裸的 -
❌ 不触发分发:
type Bar——= [T] extends [string] ? number : boolean; [T]包裹了T,整个联合被视为一个整体
写类型工具前,先检查你的泛型参数是否“裸”。否则你以为在过滤 string | number,实际只在判断 string | number 整体是否满足条件,结果往往出人意料。
用分发特性做精准类型过滤
这是最常见也最实用的场景:从 union 中筛出符合某特征的成员。核心模式是 T extends Target ? T : never,配合分发后 never 自动被剔除:
- 提取所有函数类型:
type FunctionsOnly= T extends Function ? T : never;
→FunctionsOnly void | string | () => string>→(x: number) => void | () => string - 保留可序列化的值类型(排除函数、symbol、undefined):
type Serializable= T extends (Function | symbol | undefined) ? never : T; - 注意:不能直接写
string | number extends string ? ...—— 必须封装为泛型,让 TS 主动分发
结合 infer 实现类型解构与重组
分发 + infer 能把联合类型“打散—提取—再聚合”,适合处理函数签名、数组结构等嵌套形态:
- 提取所有函数的返回值类型(并保持 union 结构):
type ReturnTypes= T extends (...args: any) => infer R ? R : never;
→ReturnTypes number | (x: string) => boolean>→number | boolean - 把多个 Promise 类型的 resolve 值取出来:
type AwaitedValues= T extends Promise ? U : never;
→AwaitedValues→| Promise > string | number - 关键点:infer 出的类型
R或U在分发过程中各自独立推导,不会跨成员混用
小心逆变位置导致的交集陷阱
当条件类型出现在函数参数这类逆变位置时,分发行为会“反转”结果:联合输入可能产出交叉输出。这容易踩坑,但也能被主动利用:
- 典型陷阱:
type Params= T extends (arg: infer P) => any ? P : never;
→Params void) | ((y: number) => void)>→string & number(即never) - 原因:函数参数是逆变的,联合类型在逆变位置会被解释为“必须同时满足所有成员”,所以参数类型变成交集
- 绕过方法:用元组包裹参数,或改用映射类型预处理;若真需要交集,这个行为反而是优势(例如构建严格接口兼容性检查)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











