infer 关键字深入
infer 关键字深入
一句话概括
infer 就是类型世界的”正则捕获组”——在条件类型的匹配模式里声明一个变量,TS 自动把匹配到的类型片段填进去,从此提取返回值、解构 Promise、拆数组元素都是一行代码的事。
核心知识点
1. infer 只能在条件类型的 extends 里用
1
2
3
4
5
// 语法:T extends 模式 ? (这里用 infer R) : else
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(name: string): string { return `Hi ${name}`; }
type R = MyReturnType<typeof greet>; // string ✅
infer 的位置 = 你想提取的类型的位置。放在返回值位置提取返回值,放在参数位置提取参数。
2. 提取函数参数(多 infer)
1
2
3
4
5
6
7
8
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
function fn(a: string, b: number, c: boolean) {}
type P = MyParameters<typeof fn>; // [string, number, boolean]
// 只提取第一个参数
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
type F = FirstArg<typeof fn>; // string
3. 提取数组/元组元素——infer 在泛型里很灵活
1
2
3
4
5
6
type ElementOf<T> = T extends (infer E)[] ? E : never;
type E = ElementOf<string[]>; // string
// 元组:取第二个元素
type Second<T extends any[]> = T extends [any, infer S, ...any[]] ? S : never;
type S = Second<[string, number, boolean]>; // number
4. 递归解包 Promise——手写 Awaited
1
2
3
4
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;
type T1 = Awaited<Promise<string>>; // string
type T2 = Awaited<Promise<Promise<number>>>; // number(递归)
5. infer + 模板字面量——字符串模式匹配
1
2
3
4
5
6
7
// 提取路由参数
type ExtractPath<T extends string> = T extends `/api/${infer P}` ? P : never;
type P = ExtractPath<"/api/users">; // "users"
// 提取域名
type ExtractHost<T extends string> = T extends `https://${infer H}/${string}` ? H : never;
type H = ExtractHost<"https://api.example.com/v1">; // "api.example.com"
6. infer 的约束(TS 4.7+)
1
2
3
// infer 后加 extends 约束
type GetNumber<T> = T extends `${infer N extends number}` ? N : never;
type N = GetNumber<"123">; // 123(number 而非 string)
「其实你每天都在用」
- **ReturnType
**:给没有导出的内部函数写类型,`type R = ReturnType ` - **Parameters
**:封装高阶函数时提取源函数的参数类型 - Awaited:
Promise.all的响应解包后的实际数据类型 - Vue 的 emit 类型推导:
defineEmits<{ submit: [data: FormData] }>()内部用 infer 提取参数 - React Hook 的类型:
useReducer需要从 reducer 函数提取 state 和 action 类型
常见误解(FAQ)
❌ 误区 1:infer 可以在任何类型定义中使用
只能在条件类型 extends 的 true 分支(? 右边)使用。它是条件类型的专属语法糖,不能出现在普通 type 里。
❌ 误区 2:联合类型 + infer 一定触发分发
当条件类型的泛型是裸类型参数且传入联合类型时才会分发。[T] extends [infer U] 套了元组就不会分发。详见上一篇「条件类型」。
❌ 误区 3:infer 声明的变量可以在整段 type 里用
infer 变量只在当前条件类型的 ? 分支(true 分支)可见。嵌套条件类型里,内层 infer 会覆盖外层同名变量。
❌ 误区 4:ReturnType<T> 传入 never 返回 never
对。never extends (...args: any[]) => infer R 由于 never 是任何类型的子类型,匹配会成功……但实际上由于分发行为(以及 never 是空联合),结果就是 never。面试小心这个坑。
一句话总结
把 infer 当成类型的”解构赋值”——在模式匹配中抓一段出来起个名字,然后想怎么用就怎么用。记住语法:extend 模式里声明,? 分支里消费。
本文由作者按照 CC BY 4.0 进行授权