TS 面试题:infer 关键字 标准回答(话术 + 极简代码)
面试口语回答(直接背,自然流畅)
面试官您好,infer 是 TypeScript 里专门用在条件类型中的关键字,核心作用就是提取、推断出某个类型内部的子类型,可以理解成类型层面的“变量声明”。
它不能单独使用,必须搭配 T extends 判断条件 ? 成功 : 失败 的条件类型语法,在 extends 右侧的待推断位置用 infer 变量名 捕获未知类型,捕获后就能在条件为真的分支里使用这个推断出来的类型。
日常最常用的场景就是:提取函数返回值类型、提取函数参数类型、提取数组元素类型、提取 Promise 内部类型,TS 内置的 ReturnType、Parameters 这些工具类型底层都是用 infer 实现的。
核心语法规则
- 只能在条件类型中使用,不能单独写
- 格式:
T extends 推断模板<infer R> ? R : 其他类型 infer R就是声明一个类型变量R,用来接住被提取的类型
高频代码示例(面试必写)
1. 手写 ReturnType:提取函数返回值类型
// 用 infer 提取函数返回值类型 R
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never
// 测试
type Fn = () => string
type Res = MyReturnType<Fn> // Res 类型为 string2. 提取函数参数类型
// 提取函数第一个参数类型
type MyParameters<T> = T extends (arg: infer P) => any ? P : never
type Fn2 = (name: string) => void
type Param = MyParameters<Fn2> // Param 类型为 string3. 提取数组元素类型
// 提取数组里的元素类型
type ElementType<T> = T extends (infer E)[] ? E : never
type Arr = number[]
type Ele = ElementType<Arr> // Ele 类型为 number4. 提取 Promise 内部类型
// 提取 Promise 包裹的类型
type PromiseType<T> = T extends Promise<infer P> ? P : never
type P = Promise<boolean>
type Data = PromiseType<P> // Data 类型为 boolean面试总结(收尾加分)
infer 本质就是类型推导的“捕获工具”,专门用来从复杂类型里抠出我们需要的子类型,是 TS 高级类型的核心关键字,也是判断候选人 TS 水平的一个小考点。
我用最通俗的面试话术 + 大白话比喻 + 极简代码,把 infer 和 泛型 的关系讲透,完全贴合你之前的理解,面试官听了直接懂你掌握了核心:
面试问题:infer 关键字和泛型有什么关系?
一、面试口语标准答案(直接背)
面试官您好,泛型和 infer 本质都是「TypeScript 的类型变量工具」,是分工不同、配合使用的黄金搭档:
- 泛型(T/U) 是提前声明的「外部类型参数」,作用是从外部传入类型,给整个函数 / 类型公用;
- infer R 是临时声明的「内部类型变量」,作用是在条件类型里「提取 / 捕获」藏在内部的子类型;
- 核心关系:infer 不能单独使用,必须依赖泛型 + 条件类型才能生效,它们一起实现了 TS 高级类型的「类型提取」能力。
二、大白话比喻(秒懂,面试加分)
把类型处理比作 「拆快递」:
- 泛型
<T>= 你提前准备好的快递盒子,从外面把「包裹(类型)」装进来,是入口传参; - infer R = 你打开盒子后,临时抓取盒子里的小物品(子类型),只在拆快递时用,是内部提取。
三、核心关系(3 个关键点)
本质相同:都是给「类型」起临时名字(类型变量),只是声明方式不一样;
分工相反:
- 泛型:外部 → 内部(主动传类型)
- infer:内部 → 外部(被动抓类型)
依赖关系:没有泛型,infer 根本用不了;infer 是泛型的「高级辅助工具」。
四、一体代码示例(最直观,同时包含泛型 + infer)
这是 TS 最经典的工具类型 ReturnType,一行代码同时用到泛型和 infer,我给你标出来:
// 👇 <T> 是【泛型】:外部传入的类型参数(入口)
type MyReturnType<T> =
// 👇 infer R 是【infer 变量】:临时提取函数的返回值类型(内部捕获)
T extends (...args: any[]) => infer R ? R : never;
// 使用:传入泛型 T = 函数类型
type Fn = () => string;
type Res = MyReturnType<Fn>; // Res = string代码拆解
- 泛型
<T>:接收外部的函数类型() => string; infer R:在类型内部,把函数的返回值string抓出来,命名为R;- 最终返回
R,完成「提取」。
五、一句话总结(面试收尾)
泛型是「传类型」,infer 是「抓类型」;泛型是入口,infer 是内部工具,两者配合实现 TS 高级类型提取。
补充:最容易混淆的点(帮你避坑)
- 泛型
<T>:可以到处用(函数、接口、类型别名); - infer
R:只能在条件类型里用,必须跟着extends; - 没有泛型,你连「要提取的目标类型」都传不进来,infer 就没有作用对象。
我给你逐字逐句大白话拆解,结合面试话术,保证你彻底懂这行代码!完全贴合你现在的理解水平,不讲复杂术语👇
面试问题:T extends (...args: any[]) => infer R 是什么意思?
一、面试口语标准答案(直接背)
面试官您好,这行代码是TypeScript 条件类型的「类型匹配模板」:
extends后面的(...args: any[]) => infer R是我定义的函数结构模板;- 作用是判断传入的泛型
T是不是一个函数; - 如果
T匹配这个函数模板,就用infer R自动提取出这个函数的返回值类型; ...args: any[]是为了兼容所有任意参数、任意个数的函数,保证通用性。
二、逐段拆解(秒懂版)
我们把这一串拆成 3 部分,放在 extends 后面的本质是「匹配规则」:
完整结构
T extends 【函数模板】 ? 提取成功返回R : 不匹配返回never第 1 部分:(...args: any[])
= 匹配「任意函数的参数」
...args:剩余参数语法,代表「不管函数有几个参数」any[]:参数类型随便,不限制- 作用:兼容所有函数(无参、1 个参、10 个参都能匹配)
第 2 部分:=>
= 函数的固定语法,代表「参数后面是返回值」
第 3 部分:infer R
= 临时声明变量 R,捕获「函数的返回值类型」
- 只有
T是函数,匹配成功时,R才会被赋值为函数的返回值类型
三、直观例子(看一遍就懂)
我们用 MyReturnType 工具类型举例,看匹配过程:
// 泛型T:外部传入的类型
// extends 后面:函数匹配模板
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 测试1:传入一个函数
type Fn = () => string;
// 1. T = Fn (是函数,匹配模板 ✅)
// 2. infer R 捕获到返回值 string → R = string
// 3. 结果:Res = string
type Res = MyReturnType<Fn>;
// 测试2:传入非函数
type Res2 = MyReturnType<number>;
// 1. T = number (不是函数,不匹配 ❌)
// 2. 结果:Res2 = never四、核心总结(面试必背)
extends后面不是代码,是「类型模板」,用来判断T是不是函数;(...args: any[])= 通吃所有函数参数;infer R= 专门抠出函数的返回值类型;整句话的意思:
如果
T是任意函数,就返回它的返回值类型R,否则返回never。
极简记忆口诀
extends 后面是函数模板,...args 匹配所有参数,infer R 提取返回值!
我用面试标准答案 + 大白话比喻 + 核心代码 + 彻底澄清误区,把你最晕的 extends / & / 交集 / 并集 一次性讲透,这是 TS 新手最容易踩坑的点,面试官也特别爱问!
面试问题:TS 里 extends 和 & 是继承、合并、交集还是并集?
一、面试口语满分回答(直接背)
面试官您好,这里有两个核心误区必须澄清:
- 接口的
extends和 类型的&(交叉类型):作用完全一样,都是「类型合并 / 扩展」,extends叫继承,&叫交叉,但它们都不是数学里的「交集」,而是把多个类型的属性全部叠加在一起; - 只有
|联合类型 才是数学里的并集; - 唯一例外:基础类型用
&交叉,才会变成数学交集(比如string & number = never)。
简单记:
extends/&= 叠加(全都要)|= 选一个(二选一)
二、逐一定义 + 大白话比喻
1. interface 的 extends → 继承(扩展合并)
- 属于面向对象语法,官方叫「继承」;
- 实际效果:子接口完全拥有父接口的所有属性,再新增自己的属性;
- 比喻:儿子继承爸爸的所有财产,再自己赚新的。
interface Father { money: number }
// 继承:Son 拥有 Father 的所有属性 + 新增 car
interface Son extends Father { car: string }
// 最终类型:{ money: number; car: string }
const son: Son = { money: 100, car: "宝马" }2. type 的 & → 交叉类型 → 本质:合并叠加
- 这是新手最大坑:TS 交叉类型
&≠ 数学交集! - 数学交集:只保留共有的部分;
- TS 交叉:要求同时满足所有类型,所以所有属性全部合并;
- 比喻:把两个盒子里的东西,全部倒进一个新盒子。
type A = { name: string }
type B = { age: number }
// 交叉&:合并 A + B 的所有属性
type C = A & B
// 最终类型:{ name: string; age: number }
const c: C = { name: "张三", age: 18 }3. 关键对比:extends 和 & 对象类型下效果完全一样
// 写法1:extends 继承
interface X {a:number}
interface Y extends X {b:string} // {a:number, b:string}
// 写法2:& 交叉
type X = {a:number}
type Y = X & {b:string} // {a:number, b:string}✅ 结论:定义对象类型时,extends 和 & 就是同义词,只是语法不同!
三、终极澄清:交集 / 并集 到底对应谁?
表格一眼看懂
表格
| 语法 | 名字 | 通俗理解 | 数学概念 | 对象类型效果 | |
|---|---|---|---|---|---|
| extends | 接口继承 | 全都要 | 无 | 合并所有属性 | |
| & | 交叉类型 | 全都要 | 无(基础类型才是交集) | 合并所有属性 | |
| ` | ` | 联合类型 | 二选一 | 并集 | 满足其一即可 |
1. 并集 = |(唯一的选一个)
type Gender = "男" | "女" // 二选一,这就是并集
type Obj = {a:number} | {b:string} // 满足一个就行2. 唯一的交集:基础类型用 &
只有string/number/boolean这种基础类型交叉,才会变成数学交集:
// 数学交集:一个值不可能既是 string 又是 number → 结果 never
type ErrorType = string & number四、面试必背总结(一句话)
extends(接口) 和&(类型):都是合并叠加,全都要,不是交集;|:并集,二选一;- 只有基础类型交叉,
&才等于数学交集(结果never)。
极简口诀(面试秒答)
继承扩展用 extends,交叉合并用 &,全都想要用这俩;二选一用竖线 |,这才叫做是并集;基础类型交叉 &,变成交集是 never!