typescript中infer常见用法
在 TypeScript 中,infer
是一个用于类型推断的关键字,通常与条件类型(Conditional Types)结合使用,用于从已有类型中提取或推断出某个子类型。它的核心作用是:在类型匹配过程中,“捕获” 符合特定结构的类型片段,并将其作为变量供后续使用。
infer
的核心特点
- 只能在条件类型的
extends
子句中使用(即T extends ... infer U ...
) - 用于 “捕获” 类型中的一部分,类似变量声明(
infer U
可以理解为 “声明一个类型变量 U,并自动推断它的值”) - 推断出的类型可以在条件类型的 “真分支”(
true
部分)中使用
基础用法示例
1. 提取函数的返回值类型
最常见的场景是从函数类型中提取返回值类型:
// 定义一个条件类型,用于提取函数的返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : T;// 使用示例
function getUser() {return { id: 1, name: '张三' };
}function sum(a: number, b: number) {return a + b;
}// 提取返回值类型
type UserResult = ReturnType<typeof getUser>; // { id: number; name: string }
type SumResult = ReturnType<typeof sum>; // number
- 原理:当
T
匹配 “函数类型” 时,infer R
会捕获该函数的返回值类型并赋值给R
- 实际开发中,TypeScript 已内置
ReturnType
,无需手动定义
2. 提取数组的元素类型
从数组类型中提取元素的类型:
// 提取数组元素类型
type ArrayItem<T> = T extends (infer U)[] ? U : T;// 使用示例
type NumberItem = ArrayItem<number[]>; // number
type StringItem = ArrayItem<string[]>; // string
type UserItem = ArrayItem<{ id: number }[]>; // { id: number }
type NotArray = ArrayItem<string>; // string(不匹配数组类型,返回原类型)
进阶用法示例
1. 提取 Promise 的 resolve 类型
从 Promise 类型中提取异步操作的结果类型:
// 提取Promise的resolve类型
type PromiseResult<T> = T extends Promise<infer R> ? R : T;// 使用示例
type ApiResult = PromiseResult<Promise<{ code: 200; data: string }>>;
// 结果:{ code: 200; data: string }type SyncResult = PromiseResult<number>;
// 结果:number(非Promise类型,返回原类型)
2. 提取函数的参数类型
从函数类型中提取参数的类型(可用于事件回调、钩子函数等场景):
// 提取函数的第一个参数类型
type FirstParam<T> = T extends (first: infer P, ...rest: any[]) => any ? P : never;// 提取函数的所有参数类型(返回元组)
type Params<T> = T extends (...args: infer P) => any ? P : never;// 使用示例
type ClickHandler = (e: MouseEvent, id: number) => void;type First = FirstParam<ClickHandler>; // MouseEvent
type AllParams = Params<ClickHandler>; // [MouseEvent, number]
3. 嵌套类型推断
处理多层嵌套的复杂类型:
// 提取嵌套数组的最内层元素类型
type DeepArrayItem<T> = T extends (infer U)[] ? DeepArrayItem<U> : T;// 使用示例
type Deep1 = DeepArrayItem<number[][]>; // number
type Deep2 = DeepArrayItem<string[][][]>; // string
type NotDeep = DeepArrayItem<number[]>; // number
注意事项
infer
只能在条件类型的extends
子句中使用,不能在其他地方单独使用- 同一条件类型中可以多次使用
infer
,推断多个不同的类型片段 - 推断结果可能是联合类型(当匹配多种可能的类型结构时)
// 多次推断示例
type FuncParts<T> = T extends ((arg: infer A) => infer R) ? { arg: A; return: R } : never;type Part = FuncParts<(name: string) => number>;
// { arg: string; return: number }