infer 推断「参数 / 返回值」这一件事
一、先给你一句「人话版结论」
infer的作用只有一个: 在extends判断成功时,把某一部分类型“抓出来”,起个名字
在函数里,它最常抓的就是:
- 参数列表
- 返回值
二、infer 推断【返回值】(最容易)
1️⃣ 标准写法(ReturnType 原理)
type MyReturnType<T> =
T extends (...args: any[]) => infer R
? R
: never逐字翻译成中文
如果 T 是函数 就把它的「返回值类型」取出来,叫 R 否则返回 never
2️⃣ 用具体例子算一遍
type Fn = (a: number, b: string) => boolean
type R = MyReturnType<Fn>TS 在脑子里做的事:
(a: number, b: string) => boolean
extends (...args: any[]) => infer R👉 匹配成功 👉 推断出:
R = boolean✅ 最终结果:
type R = boolean三、infer 推断【参数】(稍微难一点)
1️⃣ 标准写法(Parameters 原理)
type MyParameters<T> =
T extends (...args: infer P) => any
? P
: never人话版
如果 T 是函数 就把它的「参数列表」取出来,变成一个元组 否则返回 never
2️⃣ 具体例子
type Fn = (id: number, name: string) => void
type P = MyParameters<Fn>推断过程:
Fn extends (...args: infer P) => any👉 匹配成功 👉 TS 自动推断:
P = [number, string]✅ 结果:
type P = [number, string]四、为什么参数是「元组」?
因为函数参数本来就有:
- 顺序
- 个数
- 每一项的类型
(a: number, b: string)
↓
[number, string]📌 所以 Parameters 返回的一定是 元组
五、把「参数 + 返回值」放一起看(会更清楚)
type Fn = (x: number, y: string) => boolean
type Args = Parameters<Fn> // [number, string]
type Res = ReturnType<Fn> // boolean你现在已经能完整看懂这两个内置类型的原理了。
六、一个真实前端例子(你马上就通)
1️⃣ 包一层函数,类型不丢
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 |
例子一
你看不懂的是这一句 👇
type Params<T> = T extends (...args: infer P) => any ? P : never我会 拆成 4 步,每一步都“能懂了再往下”。
第 1 步:先看你「已经认识的部分」
我们先把最吓人的 infer P 拿掉,看一个你熟的👇
T extends (...args: any[]) => any ? X : Y这句话的意思是:
如果 T 是一个函数类型
- 是 → 返回
X- 否 → 返回
Y
例如:
type A = string extends Function ? 1 : 2 // 2
type B = (() => void) extends Function ? 1 : 2 // 1到这里为止,完全不神秘,对吧?
第 2 步:(...args: any[]) => any 是什么?
这是一个函数类型:
(...args: any[]) => any意思是:
任意参数、任意返回值的函数
所以:
T extends (...args: any[]) => any等价于问一句人话:
T 是不是一个函数?
第 3 步:infer P 到底是干嘛的(重点)
现在我们看核心👇
(...args: infer P) => any这句话的意思是:
如果 T 是函数,那就把它的「参数列表」抓出来, 存到一个变量 P 里
⚠️ 注意:
P不是你传的- 是 TS 自动帮你推断出来的
用一个非常具体的例子
type Fn = (a: number, b: string) => void我们代入:
Fn extends (...args: infer P) => any ? P : neverTS 在做什么?
👉 它在问:
这个
Fn能不能匹配这种函数结构?
(...args: ???) => any答案是:能
于是 TS 自动推断:
P = [number, string]第 4 步:整个类型一起算一遍(完整流程)
type Params<T> =
T extends (...args: infer P) => any
? P
: never情况 1:T 是函数
type A = Params<(x: number, y: string) => void>计算过程:
(x: number, y: string) => void
extends (...args: infer P) => any👉 匹配成功 👉 P = [number, string]
结果:
type A = [number, string]情况 2:T 不是函数
type B = Params<string>计算:
string extends (...args: infer P) => any ❌👉 走 : never
结果:
type B = never一句话版本(请记这个)
infer = “把某一部分类型,起个变量名存起来”
在这里:
infer P就是:
把函数的参数列表存到 P 里
用一句大白话翻译整行代码
type Params<T> = T extends (...args: infer P) => any ? P : never⬇️
如果 T 是函数,就返回它的参数列表; 如果不是函数,就返回 never
为什么这东西这么重要?
因为下面这些你以后天天用的,本质都是它:
Parameters<T>
ReturnType<T>
ConstructorParameters<T>例如:
type P = Parameters<(a: number, b: string) => void>
// [number, string]例子二
你不懂的是这一句 👇
type Awaited<T> = T extends Promise<infer R> ? Awaited<R> : T我会 拆成 5 个极小步骤,每一步都很“傻”,但一定能懂。
第 1 步:先看“最外层结构”(你其实已经见过)
T extends XXX ? A : B这是 类型版 if 判断:
如果 T 符合 XXX → 用 A 否则 → 用 B
到这里 没有新东西。
第 2 步:Promise<infer R> 是在干嘛?
先忘掉 infer,只看:
Promise<something>你在 JS 里很熟:
Promise<number>
Promise<string>现在这句:
T extends Promise<infer R>翻成人话是:
T 是不是一个 Promise? 如果是,把 Promise 里包的东西拿出来
第 3 步:infer R = “自动拆包”
这是关键,但我们说一句人话就够了:
infer R = “如果能匹配成功,就把里面的类型取出来,叫它 R”
举一个你 100% 熟的例子
type T = Promise<number>代入:
Promise<number> extends Promise<infer R>TS 会说:
👉 嗯,结构一样 👉 那我推断出:
R = number第 4 步:现在看 ? Awaited<R> : T
我们已经知道:
- 如果 T 是 Promise → R 是里面的值
- 如果不是 Promise → 直接返回 T
但这里不是返回 R,而是:
Awaited<R>⚠️ 重点来了:
它又把 R 再丢回 Awaited 里算一次
这一步叫:递归
第 5 步:完整走一遍(最重要)
🌰 示例 1:普通 Promise
type A = Awaited<Promise<number>>计算过程:
第一次
T = Promise<number>✔ 是 Promise 👉 R = number 👉 结果 = Awaited<number>
第二次
T = number❌ 不是 Promise 👉 走 : T
结果 = number✅ 最终结果:number
🌰 示例 2:Promise 套 Promise(这就是它存在的意义)
type B = Awaited<Promise<Promise<string>>>第一次
T = Promise<Promise<string>>
R = Promise<string>
→ Awaited<Promise<string>>第二次
T = Promise<string>
R = string
→ Awaited<string>第三次
T = string
❌ 不是 Promise
→ string✅ 最终结果:string
现在用一句大白话翻译整句代码
type Awaited<T> =
T extends Promise<infer R>
? Awaited<R>
: T⬇️
如果 T 是 Promise,就把它拆开 如果拆开后里面还是 Promise,就继续拆 直到拆到不是 Promise 为止 然后返回那个值
和 await 的关系(你马上就通)
JS 里:
const res = await await await promiseTS 里:
type Res = Awaited<typeof promise>👉 一模一样的思想
你现在只需要记住这 3 句话(真的)
- 1️⃣
infer= 帮我把里面的类型取出来 - 2️⃣
Awaited<R>= 继续拆,直到不能拆 - 3️⃣ 整个类型 = 模拟 JS 的 await 行为