TypeScript 条件类型与 infer 详解:类型系统的编程能力
条件类型让 TypeScript 类型具备"如果-否则"逻辑,infer 实现类型层面的模式匹配。本文从 T extends U ? X : Y 基础讲起,到嵌套 infer 提取、实用工具类型构建,附大量实战示例。
条件类型 T extends U ? X : Y 让类型根据条件分支选择,infer 关键字在 extends 子句中"捕获"类型变量——两者组合可以从任意复杂类型中提取部件(如函数返回值、数组元素),这正是 ReturnType、Parameters 等内置工具类型的实现原理。
类型系统的"灵活性问题"
设想你要写一个 unwrap 类型:传入 Promise<string> 得 string,传入 string 原样返回。普通泛型做不到"按条件分流"——这就是条件类型的舞台。
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type A = Unwrap<Promise<string>>; // string
type B = Unwrap<number>; // number
读法:T 能赋值给 Promise<infer U> 吗?能,把匹配到的部分记为 U 返回;不能,返回 T 本身。
条件类型的分配律
泛型参数是联合类型时,条件类型会逐个分发再合并结果:
type ToArray<T> = T extends any ? T[] : never;
type R = ToArray<string | number>; // string[] | number[]
想关闭分发,把两边包进元组:[T] extends [any] ? T[] : never——得到 (string | number)[]。
infer:类型层的模式匹配
infer 只能在条件类型的 extends 子句中使用,作用是"从这个位置提取类型":
// 提取函数返回值
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 提取数组元素
type ElementOf<T> = T extends (infer E)[] ? E : never;
// 提取第一个参数
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
多个 infer 可以同时提取多个位置,配合元组还能做"去头去尾"这类结构变换。
实战:构建实用工具类型
// 提取 Promise 链的最终值
type Awaited2<T> = T extends Promise<infer U>
? U extends Promise<any> ? Awaited2<U> : U
: T;
// 从 API 响应类型中剥离分页包装
type UnwrapPage<T> = T extends { data: infer D; total: number } ? D : T;
// 提取对象中函数类型的键名
type FunctionKeys<T> = {
[K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
}[keyof T];
递归条件类型(如 Awaited2)让类型层面也能写"循环",TS 4.5+ 对尾递归有优化。
调试技巧与边界
- 条件类型不生效时,先检查是否触发了分配律(联合类型被拆开);
- 用
type Debug<T> = { [K in keyof T]: T[K] }展开类型查看真实结构; - infer 的位置决定了提取精度——
Array<infer U>和[infer H, ...infer T]表达不同粒度; - 性能:超深递归条件类型会拖慢编辑器,复杂类型运算注意缓存中间结果。
常见问题(FAQ)
Q:infer 和泛型参数有什么区别?
泛型参数在使用处显式传入(foo<string>()),infer 在匹配时自动捕获。可以把 infer 理解为"类型层面的解构赋值"——从已有类型中拆出部件。
Q:infer 可以用在 extends 之外吗?
不行,infer 只能出现在条件类型的 extends 子句内。需要"先提取再判断"时用嵌套条件类型分两步走。
Q:什么程度算"类型体操"过度?
判断标准:类型是否服务于真实需求。库的公共 API 用条件类型提升调用体验是值得的;业务代码里炫技式的多层 infer 会让维护者崩溃——类型正确性够了就停手,可读性优先。
参考文档
本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。