infer 关键字用于在条件类型中推断类型,它可以捕获类型的某个部分供后续使用。
基本语法#
infer 只能在条件类型的 extends 子句中使用:
// TypeScript 5.x
// 基本语法:在 extends 子句中使用 infer 声明类型变量type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
// 使用function greet(): string { return 'hello'}
type GreetReturn = GetReturnType<typeof greet>// string
function add(a: number, b: number): number { return a + b}
type AddReturn = GetReturnType<typeof add>// number
// 非函数类型返回 nevertype NotFunction = GetReturnType<string>// never提取函数类型#
返回值类型#
// 提取返回值(内置 ReturnType 的实现)type MyReturnType<T extends (...args: any) => any> = T extends ( ...args: any) => infer R ? R : any
type R1 = MyReturnType<() => string> // stringtype R2 = MyReturnType<(x: number) => boolean> // booleantype R3 = MyReturnType<typeof Math.random> // number
// 处理异步函数type AsyncReturnType<T extends (...args: any) => Promise<any>> = T extends ( ...args: any) => Promise<infer R> ? R : never
async function fetchData() { return { id: 1, name: '张三' }}
type Data = AsyncReturnType<typeof fetchData>// { id: number; name: string }参数类型#
// 提取参数类型(内置 Parameters 的实现)type MyParameters<T extends (...args: any) => any> = T extends ( ...args: infer P) => any ? P : never
function example(name: string, age: number, active: boolean) {}
type Params = MyParameters<typeof example>// [name: string, age: number, active: boolean]
// 提取第一个参数type FirstParam<T extends (...args: any) => any> = T extends ( first: infer F, ...rest: any) => any ? F : never
type First = FirstParam<typeof example>// string
// 提取剩余参数type RestParams<T extends (...args: any) => any> = T extends ( first: any, ...rest: infer R) => any ? R : never
type Rest = RestParams<typeof example>// [age: number, active: boolean]
// 提取最后一个参数type LastParam<T extends any[]> = T extends [...any, infer L] ? L : never
type Last = LastParam<Parameters<typeof example>>// booleanthis 参数#
// 提取 this 参数类型type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any ? U : unknown
function greet(this: { name: string }) { console.log(`Hello, ${this.name}`)}
type GreetThis = ThisParameterType<typeof greet>// { name: string }
// 移除 this 参数type OmitThisParameter<T> = T extends (this: any, ...args: infer A) => infer R ? (...args: A) => R : T
type GreetWithoutThis = OmitThisParameter<typeof greet>// () => void提取数组类型#
// 提取数组元素类型type ElementType<T> = T extends (infer E)[] ? E : never
type E1 = ElementType<string[]> // stringtype E2 = ElementType<number[]> // numbertype E3 = ElementType<(string | number)[]> // string | number
// 提取元组第一个元素type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never
type H1 = Head<[string, number, boolean]> // stringtype H2 = Head<[]> // never
// 提取元组最后一个元素type Tail<T extends any[]> = T extends [...any[], infer L] ? L : never
type T1 = Tail<[string, number, boolean]> // boolean
// 提取除第一个外的元素type Rest<T extends any[]> = T extends [any, ...infer R] ? R : never
type R1 = Rest<[1, 2, 3]> // [2, 3]type R2 = Rest<[1]> // []
// 提取除最后一个外的元素type Init<T extends any[]> = T extends [...infer I, any] ? I : never
type I1 = Init<[1, 2, 3]> // [1, 2]提取 Promise 类型#
// 提取 Promise 内部类型type UnwrapPromise<T> = T extends Promise<infer U> ? U : T
type P1 = UnwrapPromise<Promise<string>> // stringtype P2 = UnwrapPromise<Promise<number[]>> // number[]type P3 = UnwrapPromise<string> // string(非 Promise 返回原类型)
// 深度解包(内置 Awaited 的实现)type DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T
type D1 = DeepAwaited<Promise<Promise<string>>> // stringtype D2 = DeepAwaited<Promise<Promise<Promise<number>>>> // number
// 提取 PromiseLiketype UnwrapPromiseLike<T> = T extends PromiseLike<infer U> ? U : T
const thenable = { then: (resolve: (value: string) => void) => resolve('hello'),}
type Thenable = UnwrapPromiseLike<typeof thenable>// string提取对象类型#
// 提取对象属性类型type PropertyType<T, K extends keyof T> = T extends { [P in K]: infer V } ? V : never
interface User { id: number name: string email: string}
type IdType = PropertyType<User, 'id'> // number
// 提取构造函数实例类型type InstanceType<T extends new (...args: any) => any> = T extends new ( ...args: any) => infer R ? R : any
class User { constructor(public name: string) {}}
type UserInstance = InstanceType<typeof User>// User
// 提取构造函数参数type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never
type UserParams = ConstructorParameters<typeof User>// [name: string]多个 infer#
在同一个条件中可以使用多个 infer:
// 同时提取键和值type KeyValue<T> = T extends { key: infer K; value: infer V } ? [K, V] : never
type KV = KeyValue<{ key: 'name'; value: string }>// ['name', string]
// 提取函数的参数和返回值type FunctionParts<T> = T extends (...args: infer P) => infer R ? { params: P; return: R } : never
type Parts = FunctionParts<(a: string, b: number) => boolean>// { params: [a: string, b: number]; return: boolean }
// 提取元组的头尾type HeadAndTail<T extends any[]> = T extends [infer H, ...infer T] ? { head: H; tail: T } : never
type HT = HeadAndTail<[1, 2, 3, 4]>// { head: 1; tail: [2, 3, 4] }条件推断#
结合条件类型进行复杂推断:
// 根据类型选择推断方式type Unpacked<T> = T extends (infer U)[] ? U : T extends (...args: any[]) => infer U ? U : T extends Promise<infer U> ? U : T
type T0 = Unpacked<string> // stringtype T1 = Unpacked<string[]> // stringtype T2 = Unpacked<() => string> // stringtype T3 = Unpacked<Promise<string>> // string
// 嵌套解包type DeepUnpacked<T> = T extends (infer U)[] ? DeepUnpacked<U> : T extends (...args: any[]) => infer U ? DeepUnpacked<U> : T extends Promise<infer U> ? DeepUnpacked<U> : T
type D0 = DeepUnpacked<Promise<string[]>> // stringtype D1 = DeepUnpacked<() => Promise<number[]>> // number实际应用#
类型转换工具#
// 将对象方法转换为异步版本type AsyncMethods<T> = { [K in keyof T]: T[K] extends (...args: infer A) => infer R ? (...args: A) => Promise<R> : T[K]}
interface UserService { getUser(id: number): User updateUser(user: User): void deleteUser(id: number): boolean}
type AsyncUserService = AsyncMethods<UserService>// {// getUser: (id: number) => Promise<User>// updateUser: (user: User) => Promise<void>// deleteUser: (id: number) => Promise<boolean>// }路由参数提取#
// 从路由路径提取参数type ExtractRouteParams<T extends string> = T extends `${infer Start}:${infer Param}/${infer Rest}` ? { [K in Param | keyof ExtractRouteParams<Rest>]: string } : T extends `${infer Start}:${infer Param}` ? { [K in Param]: string } : {}
type Params1 = ExtractRouteParams<'/users/:id'>// { id: string }
type Params2 = ExtractRouteParams<'/users/:userId/posts/:postId'>// { userId: string; postId: string }
type Params3 = ExtractRouteParams<'/home'>// {}事件处理器提取#
// 从组件类型提取事件处理器type ExtractEventHandlers<T> = { [K in keyof T as K extends `on${infer Event}` ? Event : never]: T[K] extends ( ...args: infer A ) => any ? A : never}
interface ButtonProps { onClick: (e: MouseEvent) => void onHover: (e: MouseEvent) => void onFocus: (e: FocusEvent) => void disabled: boolean label: string}
type ButtonEvents = ExtractEventHandlers<ButtonProps>// {// Click: [e: MouseEvent]// Hover: [e: MouseEvent]// Focus: [e: FocusEvent]// }Vue 组件 Props 推断#
// 模拟 Vue 组件 props 推断type ExtractProps<T> = T extends { props: infer P } ? P : {}
type ExtractEmits<T> = T extends { emits: infer E } ? E : {}
const component = { props: { title: String, count: Number, }, emits: ['update', 'delete'],}
type Props = ExtractProps<typeof component>// { title: StringConstructor; count: NumberConstructor }柯里化类型#
// 函数柯里化类型type Curry<F> = F extends (...args: infer A) => infer R ? A extends [infer First, ...infer Rest] ? (arg: First) => Curry<(...args: Rest) => R> : R : never
function add(a: number, b: number, c: number): number { return a + b + c}
type CurriedAdd = Curry<typeof add>// (arg: number) => (arg: number) => (arg: number) => number常见问题#
🙋 infer 可以在 extends 之外使用吗?#
// ❌ 错误:infer 只能在条件类型的 extends 子句中使用// type Wrong<T> = infer U;
// ✅ 正确type Right<T> = T extends infer U ? U : never🙋 多个 infer 同名会怎样?#
// 同名 infer 会被推断为联合类型type Foo<T> = T extends { a: infer U; b: infer U } ? U : never
type Result = Foo<{ a: string; b: number }>// string | number
// 协变位置:联合type Bar<T> = T extends { a: (x: infer U) => void; b: (x: infer U) => void } ? U : never
type Result2 = Bar<{ a: (x: string) => void; b: (x: number) => void }>// string & number = never(逆变位置)🙋 如何推断字面量类型?#
// 使用 const 断言保留字面量类型function createConfig<T extends string>(key: T) { return { key }}
const config = createConfig('api-key' as const)// { key: 'api-key' }
// 在 infer 中推断字面量type ExtractLiteral<T> = T extends `${infer L}` ? L : never
type Lit = ExtractLiteral<'hello'>// 'hello'总结#
| 用途 | 语法 | 示例 |
|---|---|---|
| 提取返回值 | (...args) => infer R | ReturnType<T> |
| 提取参数 | (...args: infer P) => any | Parameters<T> |
| 提取数组元素 | (infer E)[] | ElementType<T> |
| 提取 Promise | Promise<infer U> | Awaited<T> |
| 提取实例 | new (...args) => infer R | InstanceType<T> |
| 提取元组 | [infer H, ...infer T] | Head<T>, Tail<T> |
下一篇我们将学习模板字面量类型,了解如何在类型层面操作字符串。