infer 操作符
概述
infer 操作符是 TypeScript 条件类型中最强大的特性之一,它允许我们在条件类型中推断(infer)类型,而不是显式地声明类型。infer 操作符只能在条件类型的 extends 子句中使用,用于从复杂类型中提取和推断出我们需要的类型信息。通过 infer,我们可以实现类型级别的"模式匹配",从函数类型、数组类型、Promise 类型等复杂类型中提取出我们需要的部分。
为什么需要 infer 操作符
在没有 infer 操作符的情况下,我们无法从复杂类型中提取类型信息:
// 没有 infer:无法从函数类型中提取返回类型
type GetReturnType<T> = T extends (...args: any[]) => any ? any : never;
// 问题:只能返回 any,无法获取实际的返回类型
// 使用 infer:可以从函数类型中提取返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 优势:可以精确提取返回类型 Rinfer 操作符让我们能够:
- 提取函数返回类型:从函数类型中推断出返回类型
- 提取函数参数类型:从函数类型中推断出参数类型
- 提取数组元素类型:从数组类型中推断出元素类型
- 提取 Promise 类型:从 Promise 类型中推断出解析类型
- 类型级模式匹配:在类型层面实现复杂的类型提取和转换
基本语法
infer 的基本形式
infer 操作符的基本语法是在条件类型中使用 infer 关键字声明一个类型变量:
// 基本语法:T extends U ? infer X : Y
// 在条件类型的 true 分支中,X 是被推断出的类型
// 示例:推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;infer 的工作原理
infer 操作符的工作原理类似于类型级别的"模式匹配":
// 当 T 是函数类型时,infer R 会推断出函数的返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
type Func1 = () => string;
type Return1 = GetReturnType<Func1>; // 类型:string
type Func2 = (x: number) => boolean;
type Return2 = GetReturnType<Func2>; // 类型:boolean
// 如果 T 不是函数类型,条件不满足,返回 never
type NotFunc = string;
type Return3 = GetReturnType<NotFunc>; // 类型:never提示
infer 操作符只能在条件类型的 extends 子句中使用,并且只能在条件类型的 true 分支(? 之后)使用,不能在 false 分支(: 之后)使用。
推断函数返回类型
基本用法
最常用的场景是从函数类型中提取返回类型:
// 推断函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 使用示例
function greet(name: string): string {
return `Hello, ${name}!`;
}
type GreetReturn = GetReturnType<typeof greet>;
// 类型:string
// 处理异步函数
async function fetchUser(id: number): Promise<{ id: number; name: string }> {
return { id, name: 'John' };
}
type FetchUserReturn = GetReturnType<typeof fetchUser>;
// 类型:Promise<{ id: number; name: string }>处理泛型函数
infer 也可以处理泛型函数的返回类型:
// 推断泛型函数返回类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 泛型函数
function identity<T>(value: T): T {
return value;
}
type IdentityReturn = GetReturnType<typeof identity>;
// 类型:<T>(value: T) => T 的返回类型,即 T
// 实际使用时
type Result = GetReturnType<typeof identity<string>>;
// 类型:string处理函数重载
对于函数重载,infer 会推断出最后一个重载签名的返回类型:
// 函数重载
function process(value: string): string;
function process(value: number): number;
function process(value: string | number): string | number {
return value;
}
type ProcessReturn = GetReturnType<typeof process>;
// 类型:string | number(最后一个重载签名的返回类型)推断函数参数类型
推断所有参数类型
从函数类型中提取所有参数的类型:
// 推断函数参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never;
// 使用示例
function createUser(name: string, age: number, email: string): void {
// 实现省略
}
type CreateUserParams = GetParameters<typeof createUser>;
// 类型:[name: string, age: number, email: string]推断第一个参数类型
提取函数的第一个参数类型:
// 推断第一个参数类型
type GetFirstParameter<T> = T extends (first: infer F, ...args: any[]) => any
? F
: never;
// 使用示例
function greet(name: string, greeting?: string): void {
console.log(`${greeting || 'Hello'}, ${name}!`);
}
type GreetFirstParam = GetFirstParameter<typeof greet>;
// 类型:string推断剩余参数类型
提取除第一个参数外的所有参数类型:
// 推断剩余参数类型
type GetRestParameters<T> = T extends (first: any, ...rest: infer R) => any
? R
: never;
// 使用示例
function process(name: string, age: number, email: string): void {
// 实现省略
}
type ProcessRestParams = GetRestParameters<typeof process>;
// 类型:[age: number, email: string]推断数组和元组类型
推断数组元素类型
从数组类型中提取元素类型:
// 推断数组元素类型
type GetArrayElement<T> = T extends (infer E)[] ? E : never;
// 使用示例
type StringArray = string[];
type StringElement = GetArrayElement<StringArray>;
// 类型:string
type NumberArray = number[];
type NumberElement = GetArrayElement<NumberArray>;
// 类型:number
// 处理元组
type Tuple = [string, number, boolean];
type TupleElement = GetArrayElement<Tuple>;
// 类型:string | number | boolean(元组的所有元素类型的联合)推断元组的第一个元素
从元组类型中提取第一个元素类型:
// 推断元组的第一个元素类型
type GetFirstElement<T extends readonly any[]> = T extends [infer F, ...any[]]
? F
: never;
// 使用示例
type Tuple1 = [string, number, boolean];
type First1 = GetFirstElement<Tuple1>;
// 类型:string
type Tuple2 = [number];
type First2 = GetFirstElement<Tuple2>;
// 类型:number
type EmptyTuple = [];
type First3 = GetFirstElement<EmptyTuple>;
// 类型:never(空元组没有第一个元素)推断元组的最后一个元素
从元组类型中提取最后一个元素类型:
// 推断元组的最后一个元素类型
type GetLastElement<T extends readonly any[]> = T extends [...any[], infer L]
? L
: never;
// 使用示例
type Tuple1 = [string, number, boolean];
type Last1 = GetLastElement<Tuple1>;
// 类型:boolean
type Tuple2 = [string];
type Last2 = GetLastElement<Tuple2>;
// 类型:string推断元组的剩余元素
提取除第一个元素外的所有元素:
// 推断元组的剩余元素
type GetTail<T extends readonly any[]> = T extends [any, ...infer Rest]
? Rest
: never;
// 使用示例
type Tuple1 = [string, number, boolean];
type Tail1 = GetTail<Tuple1>;
// 类型:[number, boolean]
type Tuple2 = [string, number];
type Tail2 = GetTail<Tuple2>;
// 类型:[number]
type Tuple3 = [string];
type Tail3 = GetTail<Tuple3>;
// 类型:[]推断 Promise 类型
基本用法
从 Promise 类型中提取解析类型:
// 推断 Promise 的解析类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
// 使用示例
type PromiseString = Promise<string>;
type Unwrapped = UnwrapPromise<PromiseString>;
// 类型:string
type PromiseNumber = Promise<number>;
type Unwrapped2 = UnwrapPromise<PromiseNumber>;
// 类型:number
// 非 Promise 类型保持不变
type NotPromise = string;
type Unwrapped3 = UnwrapPromise<NotPromise>;
// 类型:string深度解包嵌套 Promise
递归解包嵌套的 Promise 类型:
// 深度解包嵌套的 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer U>
? DeepUnwrapPromise<U>
: T;
// 使用示例
type NestedPromise = Promise<Promise<Promise<string>>>;
type DeepUnwrapped = DeepUnwrapPromise<NestedPromise>;
// 类型:string
type DoublePromise = Promise<Promise<number>>;
type DeepUnwrapped2 = DeepUnwrapPromise<DoublePromise>;
// 类型:number多个 infer 推断
同时推断多个类型
在同一个条件类型中可以使用多个 infer:
// 同时推断多个类型
type GetFirstAndRest<T extends readonly any[]> = T extends [infer First, ...infer Rest]
? { first: First; rest: Rest }
: never;
// 使用示例
type Tuple = [string, number, boolean];
type Result = GetFirstAndRest<Tuple>;
// 类型:{ first: string; rest: [number, boolean] }
type Tuple2 = [string];
type Result2 = GetFirstAndRest<Tuple2>;
// 类型:{ first: string; rest: [] }推断函数参数和返回类型
同时提取函数的参数类型和返回类型:
// 同时推断函数参数和返回类型
type GetFunctionInfo<T> = T extends (...args: infer P) => infer R
? { parameters: P; returnType: R }
: never;
// 使用示例
function process(name: string, age: number): boolean {
return age > 18;
}
type ProcessInfo = GetFunctionInfo<typeof process>;
// 类型:{ parameters: [name: string, age: number]; returnType: boolean }推断数组的首尾元素
从数组中提取第一个和最后一个元素:
// 推断数组的首尾元素
type GetFirstAndLast<T extends readonly any[]> = T extends readonly [infer First, ...any[], infer Last]
? { first: First; last: Last }
: never;
// 使用示例
type Tuple1 = [string, number, boolean];
type Result1 = GetFirstAndLast<Tuple1>;
// 类型:{ first: string; last: boolean }
type Tuple2 = [string, number, boolean, number];
type Result2 = GetFirstAndLast<Tuple2>;
// 类型:{ first: string; last: number }实际应用场景
场景 1:实现内置工具类型
使用 infer 实现 TypeScript 内置的工具类型:
// 实现 ReturnType
type MyReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
// 实现 Parameters
type MyParameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
// 实现 ConstructorParameters
type MyConstructorParameters<T extends new (...args: any) => any> =
T extends new (...args: infer P) => any ? P : never;
// 实现 InstanceType
type MyInstanceType<T extends new (...args: any) => any> =
T extends new (...args: any) => infer R ? R : any;
// 使用示例
class User {
constructor(public name: string, public age: number) {}
}
type UserParams = MyConstructorParameters<typeof User>;
// 类型:[name: string, age: number]
type UserInstance = MyInstanceType<typeof User>;
// 类型:User场景 2:API 响应类型处理
从 API 函数中提取响应类型:
// API 函数定义
async function fetchUser(id: number): Promise<{
id: number;
name: string;
email: string;
}> {
// 实现省略
return { id, name: 'John', email: 'john@example.com' };
}
// 提取 API 响应类型
type ApiResponse<T> = T extends (...args: any[]) => Promise<infer R> ? R : never;
type UserResponse = ApiResponse<typeof fetchUser>;
// 类型:{ id: number; name: string; email: string }
// 创建类型安全的 API 调用函数
async function callApi<T extends (...args: any[]) => Promise<any>>(
apiFn: T,
...args: Parameters<T>
): Promise<ApiResponse<T>> {
return apiFn(...args);
}
// 使用示例
const user = await callApi(fetchUser, 1);
// user 类型:{ id: number; name: string; email: string }场景 3:事件处理器类型提取
从事件处理器映射中提取事件类型:
// 定义事件处理器类型
type EventHandlers = {
click: (event: MouseEvent) => void;
keydown: (event: KeyboardEvent) => void;
resize: (event: UIEvent) => void;
};
// 提取事件类型
type ExtractEventType<T> = T extends (event: infer E) => any ? E : never;
// 提取特定事件的事件类型
type ClickEvent = ExtractEventType<EventHandlers['click']>;
// 类型:MouseEvent
type KeydownEvent = ExtractEventType<EventHandlers['keydown']>;
// 类型:KeyboardEvent
// 创建类型安全的事件监听器
function on<T extends keyof EventHandlers>(
event: T,
handler: (event: ExtractEventType<EventHandlers[T]>) => void
): void {
// 实现省略
}
// 使用示例
on('click', (event) => {
// event 类型:MouseEvent
console.log(event.clientX, event.clientY);
});
on('keydown', (event) => {
// event 类型:KeyboardEvent
console.log(event.key, event.code);
});场景 4:深度提取嵌套类型
从复杂的嵌套类型中提取需要的类型:
// 复杂的嵌套类型
type ComplexType = {
data: {
users: Array<{
id: number;
profile: {
name: string;
avatar: Promise<string>;
};
}>;
};
};
// 提取用户数组类型
type ExtractUsers<T> = T extends { data: { users: infer U } } ? U : never;
type Users = ExtractUsers<ComplexType>;
// 类型:Array<{ id: number; profile: { name: string; avatar: Promise<string> } }>
// 提取用户元素类型
type UserElement = GetArrayElement<Users>;
// 类型:{ id: number; profile: { name: string; avatar: Promise<string> } }
// 提取头像类型并解包 Promise
type AvatarType = UserElement['profile']['avatar'];
type UnwrappedAvatar = UnwrapPromise<AvatarType>;
// 类型:string场景 5:函数组合类型推断
从函数组合中推断类型:
// 函数组合类型
type Compose<F extends (...args: any) => any, G extends (...args: any) => any> =
G extends (x: infer X) => any
? F extends (...args: infer Args) => X
? (...args: Args) => ReturnType<G>
: never
: never;
// 使用示例
function double(x: number): number {
return x * 2;
}
function toString(x: number): string {
return x.toString();
}
type Composed = Compose<typeof double, typeof toString>;
// 类型:(x: number) => string
// 实际使用
const composed: Composed = (x: number) => toString(double(x));
const result = composed(5);
// result 类型:string,值为 "10"高级用法
条件推断
结合条件类型实现更复杂的类型推断:
// 如果类型是数组,提取元素类型;如果是函数,提取返回类型
type ExtractType<T> =
T extends (infer E)[]
? E
: T extends (...args: any[]) => infer R
? R
: T;
// 使用示例
type ArrayType = ExtractType<string[]>;
// 类型:string
type FunctionType = ExtractType<() => number>;
// 类型:number
type OtherType = ExtractType<string>;
// 类型:string递归类型推断
使用 infer 实现递归类型推断:
// 递归提取所有 Promise 类型
type DeepUnwrapPromise<T> = T extends Promise<infer U>
? DeepUnwrapPromise<U>
: T;
// 递归提取数组的深层元素类型
type DeepArrayElement<T> = T extends (infer E)[]
? E extends any[]
? DeepArrayElement<E>
: E
: T;
// 使用示例
type NestedArray = string[][][];
type DeepElement = DeepArrayElement<NestedArray>;
// 类型:string类型约束推断
在 infer 中使用类型约束:
// 推断满足特定条件的类型
type ExtractStringKeys<T> = T extends { [K in infer Key]: infer Value }
? Key extends string
? Value extends string
? Key
: never
: never
: never;
// 更实用的例子:提取函数类型中特定位置的参数
type ExtractNthParameter<
T extends (...args: any[]) => any,
N extends number
> = T extends (...args: infer P) => any
? P[N] extends undefined
? never
: P[N]
: never;
// 使用示例
function example(a: string, b: number, c: boolean): void {}
type SecondParam = ExtractNthParameter<typeof example, 1>;
// 类型:number类型检查示例
常见错误
// ❌ 错误:infer 不能在条件类型的 false 分支使用
// type Wrong<T> = T extends string ? string : infer U; // 语法错误
// ❌ 错误:infer 不能在 extends 子句外使用
// type Wrong<T> = infer U; // 语法错误
// ❌ 错误:infer 变量名冲突
// type Wrong<T> = T extends (x: infer T) => any ? T : never; // T 冲突
// ❌ 错误:无法推断时返回 never
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Wrong = GetReturnType<string>; // 类型:never(因为 string 不是函数类型)正确写法
// ✅ 正确:基本 infer 用法
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// ✅ 正确:多个 infer
type GetFirstAndRest<T> = T extends [infer First, ...infer Rest]
? { first: First; rest: Rest }
: never;
// ✅ 正确:递归 infer
type DeepUnwrapPromise<T> = T extends Promise<infer U>
? DeepUnwrapPromise<U>
: T;
// ✅ 正确:条件推断
type ExtractType<T> =
T extends (infer E)[] ? E :
T extends (...args: any[]) => infer R ? R :
T;注意事项
提示
infer操作符只能在条件类型的extends子句中使用infer只能在条件类型的 true 分支(?之后)使用,不能在 false 分支使用- 可以使用多个
infer同时推断多个类型 infer变量名应该避免与外部类型参数冲突infer是类型级别的操作,不会影响运行时性能
注意
- 当条件类型不满足时,
infer推断的类型会是never - 对于函数重载,
infer会推断最后一个重载签名的类型 - 对于联合类型,
infer的行为可能不符合预期,需要特别注意 - 复杂的
infer嵌套可能会影响类型检查性能 infer推断是延迟的,只有在实际使用时才会计算
重要
infer操作符是 TypeScript 类型级编程的核心特性之一- 理解
infer对于掌握高级类型和工具类型至关重要 infer与条件类型、映射类型结合使用,可以构建强大的类型系统- 许多 TypeScript 内置工具类型(如
ReturnType、Parameters)都是基于infer实现的
信息
infer 操作符的优势:
- 类型提取:从复杂类型中精确提取需要的类型
- 类型推断:在类型层面实现模式匹配和类型推断
- 类型工具构建:构建可复用的类型工具和类型操作
- 类型安全:在编译时确保类型提取的正确性
infer 操作符的应用场景:
- 工具类型实现(ReturnType、Parameters、InstanceType 等)
- API 响应类型提取
- 事件处理器类型提取
- 函数组合类型推断
- 深度类型提取和转换
- 框架和库的类型定义