← 返回题目列表

常见 TypeScript 工具类型有哪些?

高频 中等 第 2 / 32 题 更新于 2026/07/27
TypeScript工具类型PartialPick

简化版

常见工具类型有 PartialRequiredReadonlyPickOmitRecordExcludeExtractReturnTypeParameters。它们本质上是基于泛型、keyof、映射类型和条件类型做类型变换。

详细版

示例:

interface User {
  id: number;
  name: string;
  age?: number;
}

type UserPatch = Partial<User>;
type UserPreview = Pick<User, 'id' | 'name'>;
type UserWithoutAge = Omit<User, 'age'>;
type UserMap = Record<string, User>;

函数相关:

type Fn = (id: number) => Promise<User>;
type Args = Parameters<Fn>;
type Ret = ReturnType<Fn>;

面试要能说出用途,而不只是背名字。

完整版教学

一、工具类型是类型层面的复用函数

业务中经常需要从已有类型派生新类型。比如创建用户需要一套字段,更新用户允许部分字段,列表预览只需要 id 和 name。

如果每次手写,很容易和源类型不一致。工具类型能保持关联。

二、对象字段变换

Partial<T> 把所有属性变可选,适合更新参数:

function updateUser(id: number, patch: Partial<User>) {}

Readonly<T> 把属性变只读,适合表达不可直接修改。

三、字段挑选和排除

Pick<T, K> 从类型中挑字段;Omit<T, K> 排除字段。

type PublicUser = Omit<User, 'password'>;

这比重新定义 PublicUser 更安全,因为 User 字段变化时能同步影响派生类型。

四、联合类型变换

Exclude<A, B> 从联合类型 A 中排除能赋给 B 的类型;Extract<A, B> 保留能赋给 B 的类型。

type A = Exclude<'a' | 'b', 'a'>; // 'b'

五、面试追问与工程落地

工具类型常见追问是“Partial 是深层可选吗”。内置 Partial<T> 只处理第一层属性,不会递归深入嵌套对象。如果需要深层可选,要自己写 DeepPartial 或使用工具库类型,但深层工具类型会增加类型复杂度。

还会问 Omit 怎么实现。常见实现是 Pick<T, Exclude<keyof T, K>>,这能体现工具类型之间是可组合的,而不是一堆魔法关键字。

工程里工具类型适合从源类型派生变体,但不要把接口请求、表单、数据库实体全部强行共用一个类型再疯狂 Omit/Pick。不同层模型语义不同,过度复用会让类型越来越难读。

六、拆开映射、选键和分发三个底层动作

多数内置工具类型不是编译器关键字,而是 lib.d.ts 中的泛型类型别名。对象字段变换依赖映射类型,联合筛选依赖分发条件类型,函数信息提取依赖条件类型与 infer。理解这些积木后,遇到陌生工具类型也能从定义推导结果。

type MyPartial<T> = { [K in keyof T]?: T[K] }
type MyPick<T, K extends keyof T> = { [P in K]: T[P] }
type MyExclude<T, U> = T extends U ? never : T
type MyOmit<T, K extends PropertyKey> = MyPick<T, Exclude<keyof T, K>>
工具类别代表类型核心机制
属性修饰PartialRequiredReadonly映射类型修改第一层属性标记
键集合PickOmitRecordkeyof 与键映射
联合筛选ExcludeExtractNonNullable分发条件类型
函数提取ParametersReturnTypeinfer 匹配签名
异步解包Awaited递归模拟 await 解包语义

假设 User 有 5 个字段,Pick<User, 'id' | 'name'> 只映射其中 2 个键;源字段类型变化后,这 2 个派生字段会同步变化。Partial<User> 仍只修改最外层 5 个属性,嵌套对象内部不会自动变成可选或只读。

ReturnTypeParameters 面对重载函数时通常依据最后一个签名推断,结果可能是宽泛联合。Awaited 比简单的 T extends Promise<infer U> 更完整,会递归解包 thenable,并正确处理联合与空值语义。

记忆路径:先问是在改“对象的键”、筛“联合的成员”,还是拆“函数/Promise 的结构”,再选择对应工具,而不是从名字列表里碰运气。

七、常见误区与追问

  • 误区:Partial 和 Readonly 会递归处理所有嵌套属性。 内置版本只处理第一层,深层变换需要另写递归类型并评估复杂度。
  • 误区:Omit 会在运行时删除对象字段。 工具类型只改变静态视图,不会生成删除属性的 JavaScript。
  • 误区:Record<string, T> 能保证运行时任意字符串键都有值。 它描述索引形状;动态读取的空缺风险还取决于数据和 noUncheckedIndexedAccess 配置。
  • 追问:Omit 可以怎样实现? 先用 Exclude<keyof T, K> 排除键,再用 Pick 映射保留的键。
  • 追问:Partial 为什么是浅层的? 映射只遍历 keyof T 并给当前属性加可选标记,没有递归进入 T[K]
  • 追问:Awaited 与手写 Promise 解包有什么差异? 内置类型递归模拟 await 对 PromiseLike 的处理,并覆盖联合、null、undefined 等边界。
  • 追问:什么时候不应继续叠加工具类型? 当不同层模型语义已经分离,或组合让报错和字段来源难以理解时,应定义清晰的独立类型。

八、加强记忆

工具类型的核心是“别复制类型,派生类型”。对象字段用 Partial/Pick/Omit/Record,联合筛选用 Exclude/Extract,函数信息用 Parameters/ReturnType。

能够从映射类型、keyof、条件分发和 infer 还原定义,比记住十几个名称更能应对变体追问。