TypeScript 高级工具类型实战:Pick、Omit、Partial 到 Record 的完整解析

摘要

从Pick到Record,详解TS五大工具类型------Pick挑选字段、Omit排除字段、Partial全部可选、Record字典类型、ReturnType提取返回值类型,并拆解Exclude+keyof实现Omit的内部原理。


为什么需要工具类型?

你接手一个5年前的老项目,User 接口定义了20个字段。列表页只展示 idname,详情页要显示除了 email 之外的所有信息,编辑表单需要全部字段但必须全部可选。

你的选择是:

  • A. 手写3个重复的 interface,改一个字段要同步改3处
  • B. 直接复用 User 接口,然后在代码里用 as any 强行绕过类型检查
  • C. 用 TypeScript 工具类型,一行代码搞定

正确答案是 C。

TypeScript 内置的工具类型(Utility Types)就是为这类场景设计的------基于已有类型,通过类型运算产生新类型,避免重复定义和类型污染。

typescript

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

以下所有示例都基于这个 User 接口。


Pick:从接口中挑选字段

Pick<T, K> 从类型 T 中挑选出 K 指定的字段,形成一个新的类型:

typescript

ini 复制代码
type UserPreview = Pick<User, 'id' | 'name'>;

const u: UserPreview = {
    id: 1,
    name: '张三',
};

Pick<User, 'id' | 'name'> 从 User 中只取 id 和 name 两个字段,返回的新类型只包含这两个字段。'id' | 'name' 是联合类型字符串,表示"id 或 name"。

第二个参数必须是 T 中存在的字段名,否则 TypeScript 会报错。这是类型安全的保障------不能挑选接口中不存在的字段。

典型场景:列表页只需要展示用户的 id 和 name,不需要 age 和 email,用 Pick 精准控制返回给前端的数据结构。


Omit:从接口中排除字段

Omit<T, K> 从类型 T 中排除 K 指定的字段,保留其余所有字段:

typescript

ini 复制代码
type UserSafe = Omit<User, 'email'>;

const safeUser: UserSafe = {
    id: 1,
    name: '张三',
    age: 11,
};

Omit<User, 'email'> 从 User 中去掉 email 字段,保留 id、name、age。

典型场景:API 返回用户信息时需要脱敏,去掉 email、password 等敏感字段,用 Omit 一次完成排除。


Omit 的内部实现:Exclude + keyof + Pick

Omit 的内部实现等价于 Pick<T, Exclude<keyof T, K>>。拆解这个表达式就能理解 Omit 的工作原理:

typescript

bash 复制代码
type UserKeys = keyof User;              // "id" | "name" | "age" | "email"
type KeepKeys = Exclude<UserKeys, "email">; // "id" | "name" | "age"
type MyOmitUser = Pick<User, KeepKeys>;  // 只包含 id、name、age

三步走拆解:

  1. keyof User 拿到 User 所有键的联合类型 "id" | "name" | "age" | "email"
  2. Exclude<联合类型, "email"> 从联合类型中删除 "email",剩下 "id" | "name" | "age"
  3. Pick<User, 保留的键> 从 User 中挑选出这些键,得到排除了 email 的新类型

Exclude<联合类型, 要删除的成员> 处理的是联合类型 (union),Omit<T, K> 处理的是对象接口 。Omit 内部先用 keyof 把对象键转成联合类型,再用 Exclude 删除指定键,最后用 Pick 重建对象类型。


Partial:所有字段变为可选

Partial<T> 把类型 T 的所有字段变成可选:

typescript

ini 复制代码
type PartialUser = Partial<User>;

const patchUser: PartialUser = {
    name: '李四',
    age: 12,
};
const emptyObj: PartialUser = {};

Partial<User> 相当于把 User 的每个字段都加上了 ?,变成 { id?: number; name?: string; age?: number; email?: string }

典型场景 :PATCH 请求------前端只提交修改的字段,不必传完整的 User 对象。PartialUser 类型的对象可以只传 name 和 age,也可以传空对象,TypeScript 不会报错。

Partial 对称的是 Required<T>,它将所有可选字段变为必选,适合处理配置对象默认值的场景。


Record:字典类型

Record<K, V> 创建一个键类型为 K、值类型为 V 的对象类型:

typescript

ini 复制代码
type Dict = Record<string, number>;
const obj: Dict = { a: 1, b: 2 };

Record<string, number> 表示键是字符串、值是数字的字典结构。obj 可以添加任意字符串键,值必须是数字。

典型场景:映射表,比如 HTTP 状态码和错误消息的对应关系:

typescript

typescript 复制代码
type ErrorMsgMap = Record<number, string>;

const errorMsgMap: ErrorMsgMap = { 
    400: '请求参数错误',
    401: '未登录,请重新登录',
    403: '权限不足,禁止访问',
    404: '资源找不到', 
    500: '服务器内部错误',
};

function getErrorMsg(code: number): string {
    return errorMsgMap[code] ?? '未知错误';
}

Record<number, string> 约束了键必须是数字、值必须是字符串。errorMsgMap[code] ?? '未知错误' 中的 ?? 是空值合并运算符,当 errorMsgMap[code]undefined 时返回默认值。


ReturnType:获取函数返回值类型

ReturnType<T> 提取函数类型的返回值类型:

typescript

csharp 复制代码
function fn() { return {x: 1, y: 2}; }
type FnReturn = ReturnType<typeof fn>;
// FnReturn 的类型是 { x: number; y: number }

typeof fn 获取函数的类型,ReturnType 提取该函数的返回值类型。

典型场景 :封装第三方库时,不需要手动声明返回值接口,直接从函数推导。比如封装一个 API 请求函数,用 ReturnType 推导返回值类型,保持类型与实现同步。


工具类型对比总结

| 工具类型 | 作用 | 典型场景 | 示例 |
|-----------------|-----------|------------------|--------------------------|------------|
| Pick<T, K> | 挑选指定字段 | 列表页只展示部分字段 | `Pick<User, 'id' | 'name'>` |
| Omit<T, K> | 排除指定字段 | 返回数据脱敏(去掉 email) | Omit<User, 'email'> |
| Partial<T> | 全部字段变为可选 | PATCH 请求体 | Partial<User> |
| Record<K, V> | 创建字典类型 | 状态码映射、枚举映射 | Record<number, string> |
| ReturnType<T> | 提取函数返回值类型 | API 响应类型推导 | ReturnType<typeof fn> |


总结

TypeScript 工具类型通过类型运算从已有类型派生新类型,避免了重复定义接口。

PickOmit 是"选"与"删"的对称操作------Pick 指定要保留的字段,Omit 指定要排除的字段。Omit 内部等价于 Pick<T, Exclude<keyof T, K>>,体现了 Exclude 处理联合类型、Omit 处理对象接口的分工。

Partial 让所有字段可选,适配 PATCH 场景;Required 与其对称,让所有字段必选。Record 提供字典类型约束;ReturnType 从函数推导返回值类型,保持类型与实现同步。

掌握这些工具类型,能减少大量重复的类型定义代码,让 TypeScript 项目更优雅、更安全。

相关推荐
软件开发技术深度爱好者7 小时前
在线学习实践 TypeScript 的官方Playground(演练场)使用介绍
javascript·学习·typescript
YHHLAI14 小时前
React + TypeScript 实战:从零构建颜色选择器应用
前端·react.js·typescript
YHHLAI16 小时前
[特殊字符] 从 JSX 到 TypeScript:React 开发的进化之路
javascript·react.js·typescript
Flynt17 小时前
React Router v8 升了之后,我花了两天才把项目跑起来
前端·typescript·preact
程序员小八7771 天前
后端开发初学TypeScript
前端·javascript·typescript
xm_xm_xm_12 天前
TypeScript 7 个 核心特性
前端·typescript
宇智波亚索2 天前
TypeScript 实际应用
前端·javascript·typescript
werdedeage4 天前
用 useReducer 管理多参考图生成器的前端状态
react.js·typescript
红尘散仙4 天前
TypeScript WIT Guest:类型约束与 ComponentizeJS 工程化
rust·typescript·webassembly