blog

TS 类型推断关键字 - infer

2025-05-15

接上回:到底 AwaitedReturnTypeParameters 背后用了啥魔法?

你可能会好奇,像这些内置工具类型,TypeScript 是怎么知道怎么“拆解”一个函数或一个 Promise 的?

答案就是 👇:infer


infer 是什么?

infer 是 TypeScript 的一个类型推断关键词,只能在 extends 的条件类型中使用。你可以把它理解成:

“嘿 TS,如果你能从这个结构里推断出一个符合条件的类型,那就把它命名成 X,我就用这个 X 来做点事情。”

下面马上上例子!


举个简单例子:实现 ReturnType

我们来自己手动实现一个简化版的 ReturnType

type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

来拆解一下这句话的意思:

  • T extends (...args: any[]) => infer R → 如果 T 是一个函数类型,并且它的返回值能被推断出来,那我们就用 R 表示这个返回值;
  • ? R : never → 如果推断成功,就返回推断出来的 R,否则返回 never

实测:

type Test = MyReturnType<() => number>  // 推断结果:number
type Test2 = MyReturnType<string>       // 推断失败,结果:never

再来一个进阶例子:实现 Awaited

手动实现一个简化版的 Awaited 看看:

type MyAwaited<T> = T extends Promise<infer R> ? R : T;

意思是:

  • 如果 TPromise<R>,就取出 R
  • 如果不是 Promise,那就原样返回。

实测:

type A = MyAwaited<Promise<string>>   // A = string
type B = MyAwaited<number>            // B = number

更复杂的 infer 用法:提取函数参数类型

其实 Parameters 也可以自己实现:

type MyParameters<T> = T extends (...args: infer P) => any ? P : never;

解释:

  • 如果 T 是个函数,那就推断出它的参数列表 P
  • 否则返回 never

实测:

type Fn = (id: number, name: string) => boolean;
type Args = MyParameters<Fn>   // [id: number, name: string]

就和原生 Parameters<Fn> 一模一样,是不是有点帅!


infer 的超能力:不仅能提类型,还能配合模板字符串!

有时候我们还可以对字符串做模式匹配:

type ExtractRouteParam<T> = T extends `/user/${infer Id}` ? Id : never;

比如:

type A = ExtractRouteParam<"/user/123">  // "123"
type B = ExtractRouteParam<"/post/456">  // never

是不是瞬间觉得 infer 根本是隐藏的“大Boss”? 💥


总结一下 infer 的使用套路

用法示例
提取函数返回类型T extends (...args: any[]) => infer R ? R : never
提取函数参数T extends (...args: infer P) => any ? P : never
提取 Promise 的 resolve 类型T extends Promise<infer R> ? R : T
提取数组元素类型T extends (infer U)[] ? U : never
模板字符串提取部分值T extends \/user/${infer Id}` ? Id : never`

🔚 最后小结:

  • infer 是 TS 类型系统的底层技能树之一;
  • extends 条件类型搭配用,能实现强大的模式匹配;
  • 掌握了它之后,你甚至可以写出你自己的 PickOmitPartial 等等!

如果你想彻底吃透 TypeScript 的高级类型玩法,infer 是一定绕不开的一关。下次再写类型工具的时候,完全可以试着自己造个轮子玩玩 😎


CD..