Skip to content

infer 推断「参数 / 返回值」这一件事

一、先给你一句「人话版结论」

infer 的作用只有一个: 在 extends 判断成功时,把某一部分类型“抓出来”,起个名字

在函数里,它最常抓的就是:

  • 参数列表
  • 返回值

二、infer 推断【返回值】(最容易)

1️⃣ 标准写法(ReturnType 原理)

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

逐字翻译成中文

如果 T 是函数 就把它的「返回值类型」取出来,叫 R 否则返回 never

2️⃣ 用具体例子算一遍

ts
type Fn = (a: number, b: string) => boolean

type R = MyReturnType<Fn>

TS 在脑子里做的事:

ts
(a: number, b: string) => boolean
extends (...args: any[]) => infer R

👉 匹配成功 👉 推断出:

ts
R = boolean

✅ 最终结果:

ts
type R = boolean

三、infer 推断【参数】(稍微难一点)

1️⃣ 标准写法(Parameters 原理)

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

人话版

如果 T 是函数 就把它的「参数列表」取出来,变成一个元组 否则返回 never

2️⃣ 具体例子

ts
type Fn = (id: number, name: string) => void

type P = MyParameters<Fn>

推断过程:

ts
Fn extends (...args: infer P) => any

👉 匹配成功 👉 TS 自动推断:

ts
P = [number, string]

✅ 结果:

ts
type P = [number, string]

四、为什么参数是「元组」?

因为函数参数本来就有:

  • 顺序
  • 个数
  • 每一项的类型
ts
(a: number, b: string)

[number, string]

📌 所以 Parameters 返回的一定是 元组

五、把「参数 + 返回值」放一起看(会更清楚)

ts
type Fn = (x: number, y: string) => boolean

type Args = Parameters<Fn>   // [number, string]
type Res  = ReturnType<Fn>   // boolean

你现在已经能完整看懂这两个内置类型的原理了

六、一个真实前端例子(你马上就通)

1️⃣ 包一层函数,类型不丢

ts
function withLog<T extends (...args: any[]) => any>(fn: T) {
  return (...args: Parameters<T>): ReturnType<T> => {
    console.log(args)
    return fn(...args)
  }
}
  • 📌 重点不是你能写出来
  • 📌 是你现在能看懂每一部分在干嘛

七、infer 的 3 个铁律(背下来就行)

1️⃣ infer 只能用在 extends 里 2️⃣ infer 只在条件成立时生效 3️⃣ infer 是 TS 自动推断,不是你传的

八、极简记忆法 🧠

想要什么写法
返回值(...args) => infer R
参数(...args: infer P) => any

例子一

你看不懂的是这一句 👇

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

我会 拆成 4 步,每一步都“能懂了再往下”。

第 1 步:先看你「已经认识的部分」

我们先把最吓人的 infer P 拿掉,看一个你熟的👇

ts
T extends (...args: any[]) => any ? X : Y

这句话的意思是:

如果 T 是一个函数类型

  • 是 → 返回 X
  • 否 → 返回 Y

例如:

ts
type A = string extends Function ? 1 : 2 // 2
type B = (() => void) extends Function ? 1 : 2 // 1

到这里为止,完全不神秘,对吧?

第 2 步:(...args: any[]) => any 是什么?

这是一个函数类型

ts
(...args: any[]) => any

意思是:

任意参数、任意返回值的函数

所以:

ts
T extends (...args: any[]) => any

等价于问一句人话:

T 是不是一个函数?

第 3 步:infer P 到底是干嘛的(重点)

现在我们看核心👇

ts
(...args: infer P) => any

这句话的意思是:

如果 T 是函数,那就把它的「参数列表」抓出来, 存到一个变量 P 里

⚠️ 注意:

  • P 不是你传的
  • 是 TS 自动帮你推断出来的

用一个非常具体的例子

ts
type Fn = (a: number, b: string) => void

我们代入:

ts
Fn extends (...args: infer P) => any ? P : never

TS 在做什么?

👉 它在问:

这个 Fn 能不能匹配这种函数结构?

ts
(...args: ???) => any

答案是:

于是 TS 自动推断:

ts
P = [number, string]

第 4 步:整个类型一起算一遍(完整流程)

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

情况 1:T 是函数

ts
type A = Params<(x: number, y: string) => void>

计算过程:

ts
(x: number, y: string) => void
extends (...args: infer P) => any

👉 匹配成功 👉 P = [number, string]

结果:

ts
type A = [number, string]

情况 2:T 不是函数

ts
type B = Params<string>

计算:

ts
string extends (...args: infer P) => any ❌

👉 走 : never

结果:

ts
type B = never

一句话版本(请记这个)

infer = “把某一部分类型,起个变量名存起来”

在这里:

ts
infer P

就是:

把函数的参数列表存到 P 里

用一句大白话翻译整行代码

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

⬇️

如果 T 是函数,就返回它的参数列表; 如果不是函数,就返回 never

为什么这东西这么重要?

因为下面这些你以后天天用的,本质都是它:

ts
Parameters<T>
ReturnType<T>
ConstructorParameters<T>

例如:

ts
type P = Parameters<(a: number, b: string) => void>
// [number, string]

例子二

你不懂的是这一句 👇

ts
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T

我会 拆成 5 个极小步骤,每一步都很“傻”,但一定能懂。

第 1 步:先看“最外层结构”(你其实已经见过)

ts
T extends XXX ? A : B

这是 类型版 if 判断

如果 T 符合 XXX → 用 A 否则 → 用 B

到这里 没有新东西

第 2 步:Promise<infer R> 是在干嘛?

先忘掉 infer,只看:

ts
Promise<something>

你在 JS 里很熟:

ts
Promise<number>
Promise<string>

现在这句:

ts
T extends Promise<infer R>

翻成人话是:

T 是不是一个 Promise? 如果是,把 Promise 里包的东西拿出来

第 3 步:infer R = “自动拆包”

这是关键,但我们说一句人话就够了

infer R = “如果能匹配成功,就把里面的类型取出来,叫它 R”

举一个你 100% 熟的例子

ts
type T = Promise<number>

代入:

ts
Promise<number> extends Promise<infer R>

TS 会说:

👉 嗯,结构一样 👉 那我推断出:

ts
R = number

第 4 步:现在看 ? Awaited<R> : T

我们已经知道:

  • 如果 T 是 Promise → R 是里面的值
  • 如果不是 Promise → 直接返回 T

但这里不是返回 R,而是:

ts
Awaited<R>

⚠️ 重点来了:

它又把 R 再丢回 Awaited 里算一次

这一步叫:递归

第 5 步:完整走一遍(最重要)

🌰 示例 1:普通 Promise

ts
type A = Awaited<Promise<number>>

计算过程:

第一次

ts
T = Promise<number>

✔ 是 Promise 👉 R = number 👉 结果 = Awaited<number>

第二次

ts
T = number

❌ 不是 Promise 👉 走 : T

ts
结果 = number

✅ 最终结果:number

🌰 示例 2:Promise 套 Promise(这就是它存在的意义)

ts
type B = Awaited<Promise<Promise<string>>>

第一次

ts
T = Promise<Promise<string>>
R = Promise<string>
→ Awaited<Promise<string>>

第二次

ts
T = Promise<string>
R = string
→ Awaited<string>

第三次

ts
T = string
❌ 不是 Promise
→ string

✅ 最终结果:string

现在用一句大白话翻译整句代码

ts
type Awaited<T> =
  T extends Promise<infer R>
    ? Awaited<R>
    : T

⬇️

如果 T 是 Promise,就把它拆开 如果拆开后里面还是 Promise,就继续拆 直到拆到不是 Promise 为止 然后返回那个值

await 的关系(你马上就通)

JS 里:

ts
const res = await await await promise

TS 里:

ts
type Res = Awaited<typeof promise>

👉 一模一样的思想

你现在只需要记住这 3 句话(真的)

  • 1️⃣ infer = 帮我把里面的类型取出来
  • 2️⃣ Awaited<R> = 继续拆,直到不能拆
  • 3️⃣ 整个类型 = 模拟 JS 的 await 行为