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 会让维护者崩溃——类型正确性够了就停手,可读性优先。


参考文档

本文依据公开文档整理,示例需按项目依赖版本验证;不将文档中的性能描述视为本项目实测结果。

延伸阅读

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台