讲解
条件类型是类型系统的三元表达式:T extends U ? X : Y——如果 T 是 U 的子类型,结果就是 X,否则是 Y。它让类型具备了「分情况讨论」的能力。type IsString = T extends string ? true : false,IsString<"a"> 是 true,IsString<1> 是 false。注意条件类型对裸类型参数有分配律:传入联合类型时会分发到每个成员再合并结果——T extends string ? T : never 作用于 string | number 时得到 string(number 成员被映射成 never 后被联合吸收)。Exclude 的实现正是利用了这个特性。
infer 关键字在条件类型的 extends 分支里「占位并捕获」:T extends Array ? Item : never 表示「如果 T 是某种数组,把它的元素类型捕获为 Item 并返回」。由此一行就能写出 ElementType;同理 T extends (...args: never[]) => infer R ? R : never 就是 ReturnType 的雏形。infer 可以出现在参数、返回值、Promise 包装、元组任意位置,是拆解复杂类型的瑞士军刀。
条件类型与映射类型组合,就具备了写「类型级程序」的完整能力。但请把复杂度留给库作者:业务代码里条件类型主要用于理解和少量封装,大量递归类型会显著拖慢编译并让报错天书化。读到这个层级,目标是「看得懂 lib.d.ts 和主流库的类型源码」。
示例
条件类型与分配律:
type OnlyString<T> = T extends string ? T : never;
type A = OnlyString<string>; // string
type B = OnlyString<string | number | boolean>; // 分配后只剩 string
const a: A = "必须是字符串";
const b: B = "联合里也只有字符串存活";
console.log(a, b);
infer 捕获数组元素与函数返回值:
type ElementOf<T> = T extends readonly (infer E)[] ? E : never;
type MyReturn<T> = T extends (...args: never[]) => infer R ? R : never;
type Num = ElementOf<number[]>; // number
type First = ElementOf<[string, boolean]>; // string | boolean
function stats() {
return { count: 3, ok: true };
}
type StatsResult = MyReturn<typeof stats>; // { count: number; ok: boolean }
const r: StatsResult = stats();
const n: Num = 7;
const f: First = "可以是两者之一";
console.log(r.count, n, f);
infer 解包 Promise:
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type Raw = UnwrapPromise<Promise<string>>; // string
type Already = UnwrapPromise<number>; // number(不是 Promise 原样返回)
async function load(): Promise<Raw> {
return "数据";
}
const plain: Already = 1;
console.log(plain, typeof load);
常见坑
- 分配律意外生效:想整体判断联合类型时,把裸 T 包成 [T] extends [U] 阻止分发——这是标准技巧。
- infer 捕获位置想错:infer 只在 extends 子句里合法,且捕获的是「匹配位置」的类型;写在 then 分支里直接报错。
- 递归条件类型拖垮编译:类型级递归深度有上限(约 50 层实例化),复杂变换优先换成迭代式写法或简化需求。
- 把条件类型当运行时逻辑:T extends string 判断的是类型不是值;运行时的 typeof 判断和它没有任何联动。
小结
条件类型 = 类型级 if,裸参数遇联合自动分发;infer 在匹配中捕获局部类型。至此类型体操的核心件已齐。下一章回到地面:类型断言与 satisfies 的正确使用。