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、字符串上提取类型,再结合递归与条件类型,就能读懂并写出绝大多数高级类型工具。