TypeScript 面试必会:工具类型全家桶,从 keyof 到手写 Partial

从一个真实的面试题开始

面试官甩过来一道题:

"Omit<T, k> 等价于 Pick<T, Exclude<keyof T, k>>,这句话你能解释一下吗?"

如果只是背结论,一追问就露馅。但如果你自己手写过一遍 这个等价实现,就会知道它背后是三条环环相扣的链:keyof 取键 → Exclude 删键 → Pick 留键。这篇文章就用面试题的线索,把 TS 工具类型这一块串成一张网,面试问哪个都不慌。

先准备一张表,后面所有问题都在这张表上:

ts 复制代码
interface User {
  id: number;
  name: string;
  age: number;
  email: string;
}

面试题一:Omit<T, k> 为什么等价于 Pick<T, Exclude<keyof T, k>>?

把右边拆成三步,每一步都很直白:

bash 复制代码
keyof T                        Exclude<keyof T, "email">        Pick<T, 剩下的键>
   ↓                                ↓                                ↓
"id" | "name" | "age" | "email"   →  "id" | "name" | "age"   →  { id; name; age }
  1. keyof T 拿到所有键 的联合类型:"id" | "name" | "age" | "email"
  2. Exclude 删掉要排除的键 ,剩下要保留的键:去掉 "email"
  3. Pick 把剩下的键从 T 里挑出来,形成新类型

这三步合起来,就是 Omit 的效果------去掉 email 字段。TS 内置的 Omit 实现就是这么写的:

ts 复制代码
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

面试答案:Omit 是"删掉几个字段",而 Pick 是"保留几个字段",两者互为反面。Omit 的底层就是先 keyof 取全部键,再 Exclude 删掉不要的,最后 Pick 挑剩下的。删掉的反面就是保留剩下的。


面试题二:keyof 是工具类型吗?

不是。 这是一个常见的概念混淆。

  • keyof 是类型操作符 (语法关键字,像 typeof、in、infer),它不"产生"一个工具,而是查询一个类型的所有键,返回联合类型。
  • 工具类型 (Utility Types)是 TS 内置的一批"类型函数",比如 Pick、Omit、Partial。

所以你会发现一个规律:keyof 是一切对象型工具类型的地基 。Pick、Omit、Partial 的手写实现,全部要从 keyof T 开始。


面试题三:工具类型有哪些?各自操作什么?

工具类型看起来多,但其实每一种都只操作某一类"对象"。按操作对象分类,一下就记住了:

操作"对象类型字段"的

工具类型 作用 示例
Pick<T, K> 按键挑选若干字段 `Pick<User, "id"
Omit<T, K> 按键去掉若干字段 Omit<User, "email">
Partial<T> 所有字段变可选 Partial<User>
Required<T> 所有字段变必选(Partial 反面) Required<Partial<User>>
Readonly<T> 所有字段变只读 Readonly<Config>
Record<K, V> 用键类型 + 值类型现造一个对象类型 Record<number, string>

操作"联合类型"的

工具类型 作用 示例
Exclude<T, U> 删掉 U 中出现的成员(差集) `Exclude<"a"
Extract<T, U> 只保留两边都有的成员(交集) `Extract<"a"
NonNullable<T> 去掉 null 和 undefined `NonNullable<string

操作"函数类型"的

工具类型 作用 示例
ReturnType<T> 提取函数返回值类型 ReturnType<typeof fn>
Parameters<T> 提取函数参数类型(元组) Parameters<(a: string) => void> → [string]
InstanceType<T> 提取构造函数的实例类型 InstanceType<typeof Date>

解 Promise 的

工具类型 作用 示例
Awaited<T> 把 Promise<T> 拆成 T(可多层) Awaited<Promise<Promise<string>>> → string

记忆框架:成对出现 + 按对象分类

vbnet 复制代码
对象类型字段:Partial ↔ Required、Readonly
对象类型键  :Pick ↔ Omit、Record(造)
联合类型    :Exclude ↔ Extract、NonNullable
函数类型    :ReturnType ↔ Parameters、InstanceType
异步        :Awaited

面试答案:TS 工具类型大多成对出现,记一个就记住它的反面。再按"操作对象"分类------对象类型、联合类型、函数类型、Promise------整套工具类型就织成网了,而不是一堆孤立的名字。


面试题四:手写一个 Partial(映射类型)

面试常让手写工具类型,本质只考一个语法:映射类型(Mapped Type)。

ts 复制代码
type MyPartial<T> = { [K in keyof T]?: T[K] };

逐词拆解:

片段 含义
T 传入的对象类型,如 User
keyof T 取所有键 → `"id"
[K in keyof T] 遍历:K 依次等于联合里的每个键
?: 给每个属性加上可选修饰符
T[K] 索引访问类型:取 K 对应的值类型

对 User 展开就是:

ts 复制代码
type MyPartial<User> = {
  id?: number;      // K = "id",    T["id"]    = number
  name?: string;    // K = "name",  T["name"]  = string
  age?: number;     // K = "age",   T["age"]   = number
  email?: string;   // K = "email", T["email"] = string
};

[K in keyof T] 是"循环",T[K] 在循环里逐个取值类型,?: 统一加可选。T[K] 索引访问类型 是一个单独的重要概念:像 user["name"] 读值一样,User["name"] 在类型层面读类型。

同样的骨架,换几个修饰符就是另一个工具类型:

ts 复制代码
type MyPartial<T>  = { [K in keyof T]?: T[K] };              // 全部可选
type MyReadonly<T> = { readonly [K in keyof T]: T[K] };      // 全部只读
type MyRequired<T> = { [K in keyof T]-?: T[K] };             // 全部必选(-? 去掉可选)
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };      // 只挑 K 这些键
type MyRecord<K extends keyof any, V> = { [P in K]: V };     // 现造一个对象

面试答案:工具类型的底层语法就两样------映射类型 ({ [K in keyof T]: ... })管对象型工具类型,条件类型 + infer (T extends U ? X : Y)管联合型和函数型。手写练习时,把这五个变体写一遍,映射类型就焊死在脑子里了。


面试题五:ReturnType<typeof fn> 里的 typeof 是什么?

这道题最容易卡住人,因为同一个 typeof 有两副面孔:

场景 写法 结果
JS 运行时 typeof fn 字符串 "function"
TS 类型层面 typeof fn fn 的类型 () => { x1: number; y: number }

在 ReturnType<typeof fn> 里,typeof 出现在类型位置,所以走的是类型查询 ------把一个值 (fn)转成它的类型 。为什么需要这一步?因为 ReturnType 只接收"类型"不接收"值",而 fn 是个值。

ts 复制代码
function fn() {
  return { x1: 1, y: 2 };
}
// typeof fn = () => { x1: number; y: number }
type fnReturn = ReturnType<typeof fn>;
// fnReturn   = { x1: number; y: number }

ReturnType 做的事就是"把这个函数类型里返回值的类型抠出来"。为什么不直接手写 { x1: number; y: number }?因为函数返回类型一变,手写的地方容易漏改。用 ReturnType 让实现成为唯一事实来源 ,类型跟着走。ReturnType 的底层实现靠 infer:

ts 复制代码
type MyReturnType<T extends (...args: any) => any>
  = T extends (...args: any) => infer R ? R : never;

面试答案:typeof 在类型位置是把值变成类型,ReturnType 是从函数类型里抠出返回值类型。两者合起来就是"让实现来推导返回类型",保持单一事实来源。


面试题六:?? 和 || 有什么区别?

这题虽然不属于工具类型,但面试常挨着问。两者的默认值判断标准完全不同:

  • || 看假值 (falsy):0、""、false、NaN 都会触发
  • ?? 只看 null / undefined
ts 复制代码
const n = 0;
const a = n ?? 10;   // 0   ------ 0 不是 null/undefined,保留
const b = n || 10;   // 10  ------ 0 是 falsy,被替换成默认值

实战场景(http 状态码映射):

ts 复制代码
const errorMessage: Record<number, string> = {
  400: "请求参数错误",
  401: "未登录,请先登录",
  404: "资源找不到",
};

function getErrMsg(code: number): string {
  return errorMessage[code] ?? "未知错误";  // 这里必须用 ??
}

如果某个 key 存在但值是 "" 或 0 这类合法值,|| 会误判成"缺失"替换成默认值,?? 不会。

顺带把"带问号的运算符"全家桶补全:

写法 名字 作用
?. 可选链 链上安全访问,中途为空返回 undefined 不报错
?? 空值合并 左边是 null/undefined 才取右边
??= 空值合并赋值 左边是空值才赋值(a ??= b)
` =/&&=`
? : 三元条件 简写 if/else 表达式

(注意:?= 不是合法的 JS/TS 运算符,别被面试题绕进去。)

面试答案:?? 只在 null/undefined 时取默认值,|| 在所有 falsy 值都取------处理 0、""、false 这类合法值时用 ?? 更安全。


本篇总结

面试题 一句话答案
Omit 的等价实现 Omit = Pick 挑剩下的 = 先 keyof 取键,再 Exclude 删键,最后 Pick 留键
keyof 是工具类型吗 不是,是类型操作符,是对象型工具类型的地基
工具类型有哪些 按对象分类:对象字段 / 联合类型 / 函数类型 / Promise,且大多成对
手写 Partial 映射类型 { [K in keyof T]?: T[K] },换修饰符就是 Required/Readonly/Pick
ReturnType 里的 typeof 类型层面的 typeof 把"值"变成"类型",ReturnType 抠出返回值
?? 和 || ?? 只看 null/undefined,|| 看所有 falsy
相关推荐
怕浪猫7 分钟前
个人开发者、OPC 个体狂喜的免费资源网站合集,额度不是免费试用:1300个开发者资源
面试·架构·github
此时不提桶,更待何时14 小时前
05-03-B-ClickHouse与OLAP面试与生产事故实战
clickhouse·面试·nosql
此时不提桶,更待何时15 小时前
03-04-B-连接池与DB治理面试与生产事故实战
mysql·面试
kaiyou202616 小时前
数据分析岗面试,如何把考证学到的知识讲成业务案例?
面试·数据挖掘·数据分析
智购科技自动售货机工厂20 小时前
数字人民币硬钱包支付失败,排查发现是NFC读卡器功率不足~YH
python·面试·架构·eclipse·emacs
Sam_Deep_Thinking1 天前
如何理解java的信号量
java·后端·面试·程序员
seowmt1 天前
结构化数据和自然语言描述哪个更管用?答案分场景
人工智能·google·typescript·go语言·schema
王中阳Go1 天前
面试官问"你怎么证明它有效",200个转AI的后端没几个答得上来
人工智能·后端·面试
进击的明明1 天前
TypeScript速通笔记(上)
前端·面试·typescript
老马识码1 天前
[agent开发面试]上下文工程与压缩:Agent 的稀缺资源
人工智能·面试