TS 类型推断关键字 - infer
2025-05-15
接上回:到底 Awaited
、ReturnType
、Parameters
背后用了啥魔法?
你可能会好奇,像这些内置工具类型,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;
意思是:
- 如果
T
是Promise<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
条件类型搭配用,能实现强大的模式匹配; - 掌握了它之后,你甚至可以写出你自己的
Pick
、Omit
、Partial
等等!
如果你想彻底吃透 TypeScript 的高级类型玩法,infer
是一定绕不开的一关。下次再写类型工具的时候,完全可以试着自己造个轮子玩玩 😎