← 返回题目列表

条件类型是什么?infer 有什么作用?

高频 困难 第 21 / 32 题 更新于 2026/07/27
TypeScript条件类型infer

简化版

条件类型形如 T extends U ? X : Y,表示类型层面的条件判断。infer 用于在条件类型中推断某一部分类型,常用于提取函数返回值、Promise 内部值、数组元素类型。

详细版

条件类型:

type IsString<T> = T extends string ? true : false;

infer 示例:

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

如果 T 是函数类型,infer R 会推断返回值类型。

提取 Promise 值:

type AwaitedValue<T> = T extends Promise<infer R> ? R : T;

条件类型遇到联合类型会分发,这是很多工具类型的基础。

完整版教学

一、条件类型是类型系统里的 if

普通 JS 可以写:

condition ? a : b

TS 类型层面也可以根据类型关系选择不同结果:

type ToArray<T> = T extends any ? T[] : never;

二、extends 在条件类型里表示可赋值判断

T extends U 不是类继承,而是判断 T 是否可以赋值给 U。

type A = 1 extends number ? true : false; // true

三、infer 是局部类型变量

infer 只能在条件类型的 extends 子句中使用。它像是告诉 TS:这里有一块类型,请帮我推出来。

type ElementType<T> = T extends (infer U)[] ? U : T;

ElementType<string[]> 得到 string。

四、分发条件类型

当 T 是裸类型参数并传入联合类型时,条件类型会对联合成员逐个计算:

type ToArray<T> = T extends any ? T[] : never;
type R = ToArray<string | number>; // string[] | number[]

这就是 ExcludeExtract 等工具类型能工作的原因。

五、面试追问与工程落地

条件类型常见追问是“如何关闭分发”。把类型参数包在元组里即可:

type NoDistribute<T> = [T] extends [string] ? true : false;

这样传入联合类型时不会逐个成员分发,而是把整个联合一起判断。

还会问 infer 能不能推多个位置。可以,甚至可以通过多个 infer 提取参数和返回值。但如果推断位置冲突,结果可能变成联合或更宽的类型。写复杂类型时要用小例子验证,不要凭感觉。

工程里条件类型适合封装库类型、请求返回解包、组件泛型推断。业务代码不要过度堆叠条件类型,否则报错信息会变得难以理解。类型体操的终点应该是提升调用体验,而不是炫复杂。

六、按代入、分发、匹配三步手算结果

手算条件类型先看被检查的一侧是不是“裸类型参数”。如果是,并且实际参数是联合类型,就先按成员分发;每个成员再做 extends 可赋值判断,最后把分支结果重新联合。把 T 包进单元素元组后,检查对象变成整个联合,分发随之关闭。

type Mark<T> = T extends string ? `S:${T}` : 'N'
type A = Mark<'a' | 'b' | 1>
// Mark<'a'> | Mark<'b'> | Mark<1>
// 'S:a' | 'S:b' | 'N'

type Whole<T> = [T] extends [string] ? 'all string' : 'not all string'
type B = Whole<'a' | 1> // 'not all string'

| 步骤 | Mark<'a' | 'b' | 1> 的结果 | | --- | --- | | 分发前 | 1 个含 3 个成员的联合 | | 逐项判断 | 'a''b' 命中 true,1 命中 false | | 重新合并 | 'S:a' | 'S:b' | 'N' |

infer 是匹配成功时声明的局部类型变量,可以在一次模式中推断多个位置。对重载函数做返回值推断时,TypeScript 通常从最后一个、最宽泛的调用签名推断,不会逐个重载做调用解析,因此结果可能比某个具体调用更宽。

心法:看到条件类型先圈出 extends 左侧;裸 T 决定是否分发,infer 只负责在匹配成功的结构里取出部件。

七、常见误区与追问

  • 误区:条件类型中的 extends 一定表示继承。 这里判断的是类型可赋值关系,不是运行时类继承。
  • 误区:任何联合类型都会触发分发。 只有被检查位置是裸类型参数且代入联合时才分发,[T]Promise<T> 等包装会改变行为。
  • 误区:infer 可以在任意类型别名中声明。 它只能出现在条件类型 extends 子句的匹配位置。
  • 追问:如何关闭分发条件类型? 把判断两侧包成元组,如 [T] extends [U] ? X : Y,让整个联合参与一次判断。
  • 追问:Exclude 为什么能过滤联合成员? 它对联合逐项分发,把可赋给排除集合的成员映射成 never,联合中 never 随后消失。
  • 追问:infer 能同时提取参数和返回值吗? 可以,在函数模式中分别写 infer Pinfer R,但必须明确约束和失败分支。
  • 追问:复杂条件类型为什么会拖累维护? 多层分发和递归会放大实例化数量、拉长报错信息,也可能触发类型实例化过深限制。

八、加强记忆

条件类型是类型里的三元表达式,infer 是在匹配过程中“顺手取出一块类型”。复杂工具类型很多都建立在条件类型、分发和 infer 上。

手算时坚持“识别裸参数、展开联合、逐项匹配、合并结果”四步,就能解释多数 infer 与工具类型题。