TypeScript infer 关键字

infer 意为"推断",只能在条件类型的 extends 子句中使用,用来从某个类型里"提取"出一部分并声明为类型变量。它是类型体操的核心引擎,ReturnType、Awaited 等内置工具类型都由它实现。

基本语法

infer 的写法是 T extends 某种模式 ? 使用 infer 变量 : 兜底

// 若 T 是函数类型,就提取它的返回类型 R
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser() {
  return { id: 1, name: "张三" };
}

type R = MyReturnType<typeof getUser>;
// R = { id: number; name: string }
const data: R = { id: 1, name: "张三" };
console.log(data); // 输出:{ id: 1, name: '张三' }

匹配成功走真分支并得到 R,匹配失败走假分支(这里为 never)。

提取数组元素类型

把"某个类型的数组"拆成"单个元素类型":

type ElementOf<T> = T extends (infer E)[] ? E : never;

type S = ElementOf<string[]>; // string
type N = ElementOf<number[]>; // number

配合元组还能取出第一个、最后一个元素:

type First<T> = T extends [infer F, ...unknown[]] ? F : never;
type Last<T> = T extends [...unknown[], infer L] ? L : never;

type F = First<["a", 1, true]>; // "a"
type L = Last<["a", 1, true]>; // true

递归解开 Promise

infer 变量可以在真分支里继续参与递归,逐层剥掉 Promise 外壳:

type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

type P1 = Awaited<Promise<number>>; // number
type P2 = Awaited<Promise<Promise<string>>>; // string
type P3 = Awaited<number>; // number,非 Promise 原样返回

解析字符串模板

infer 与模板字面量类型组合,能在编译期拆分字符串:

type Ext<T extends string> = T extends `${infer _}.${infer E}` ? E : never;

type A = Ext<"report.pdf">; // "pdf"
type B = Ext<"notes.txt">; // "txt"
type C = Ext<"README">; // never

type Boot<T extends string> = T extends `on${infer Rest}` ? Rest : never;
type D = Boot<"onClick">; // "Click"

使用注意事项

infer 只能出现在条件类型 extends 子句的"待匹配位置",不能单独声明;同一模式可写多个 infer,且真分支里每个变量都能直接用。匹配不到时记得给假分支一个合理兜底(常用 never),否则泛型会推导失败。

小结

infer 解决"从复杂类型里取出子类型"这一核心问题。先学会用它在函数、数组、Promise、字符串上提取类型,再结合递归与条件类型,就能读懂并写出绝大多数高级类型工具。

笔记加载中…