一文搞懂 TypeScript 常用泛型工具类型(Record、Partial、Omit、Pick 等)
TypeScript 内置了一批 泛型工具类型 ,比如
Record
、Partial
、Omit
、Pick
等,它们能基于已有类型快速生成新类型,避免重复定义,让类型既安全又灵活。如果你停留在只会"用",却不太理解 为什么会出现 、原理是什么 、最佳实践如何选型 。本文就带你从 痛点 → 工具类型 → 实战 → 原理,一次性搞透。
1. 为什么会出现?
早期 TypeScript 开发的痛点:
- 相似的类型要重复写多份
- 为了适配不同场景,需要频繁复制粘贴类型定义
- 一旦源类型变动,所有相关类型都要手动更新
泛型工具类型就是为了解决这些问题:
- 减少重复:用组合和派生代替重复声明
- 提升安全:编译器帮你检查类型错误
- 更灵活:业务变动时派生类型自动适配
2. 常用工具类型列表
工具类型 | 作用 | 简述原理 |
---|---|---|
Record<K, T> |
将键集合 K 映射到类型 T |
映射类型批量生成属性 |
Partial<T> |
将所有属性设为可选 | ? 修饰符映射 |
Omit<T, K> |
从 T 排除 K 属性 |
Pick + Exclude |
Pick<T, K> |
只保留 K 指定属性 |
映射类型选择 |
Required<T> |
将所有属性设为必填 | 去掉 ? 修饰符 |
Readonly<T> |
将所有属性设为只读 | readonly 修饰符映射 |
Exclude<T, U> |
从联合类型 T 排除类型 U |
条件类型 |
Extract<T, U> |
从联合类型 T 提取 U 类型 |
条件类型 |
NonNullable<T> |
排除 null 和 undefined |
条件类型 |
ReturnType<F> |
获取函数返回值类型 | infer 推导 |
InstanceType<C> |
获取构造函数实例类型 | infer 推导 |
3. 使用场景与原理
3.1 Record:权限表
ts
type Role = 'admin' | 'editor' | 'viewer';
type Permission = 'read' | 'write' | 'delete';
const rolePermissions: Record<Role, Permission[]> = {
admin: ['read', 'write', 'delete'],
editor: ['read', 'write'],
viewer: ['read']
};
原理 :Record<K, T>
本质就是"用 K 的所有键生成一组属性":
ts
type Record<K extends keyof any, T> = {
[P in K]: T;
}
3.2 Partial:更新用户信息
ts
interface UserProfile {
id: number;
name: string;
email: string;
}
function updateUser(id: number, updates: Partial<UserProfile>) {
// ...
}
updateUser(1, { email: 'new@example.com' });
原理 :通过映射类型给所有属性加上 ?
:
ts
type Partial<T> = {
[P in keyof T]?: T[P];
}
3.3 Omit:去除敏感字段
ts
interface User {
id: number;
name: string;
password: string
}
type SafeUser = Omit<User, 'password'>;
原理 :其实就是 Pick + Exclude 的组合:
ts
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>
3.4 Pick:只要部分字段
ts
type UserNameEmail = Pick<UserProfile, 'name' | 'email'>;
原理 :只保留 K
中的键:
ts
type Pick<T, K extends keyof any> = {
[P in K]: T[P]
}
3.5 Required:接口必填化
ts
type StrictUser = Required<Partial<UserProfile>>;
3.6 Readonly:让属性只读
ts
const config: Readonly<{ apiUrl: string }> = {
apiUrl: 'https://api.example.com'
};
config.apiUrl = '' // 报错
3.7 Exclude & Extract:联合类型过滤
ts
type T = 'a' | 'b' | 'c';
type WithoutB = Exclude<T, 'b'>; // 'a' | 'c'
type OnlyB = Extract<T, 'b'>; // 'b'
对比:
- Pick/Omit 好比一个 对象裁剪器 :我有一张"用户表单",我要么只留下
name/email
(Pick),要么去掉password
(Omit)。 - Exclude/Extract 好比一个 集合过滤器 :我有一个集合
{'a','b','c'}
,我去掉b
(Exclude)或只要b
(Extract)。
3.8 NonNullable:去掉 null/undefined
ts
type Value = string | null | undefined;
type SafeValue = NonNullable<Value>; // string
3.9 ReturnType & InstanceType:类型推导
ts
function getUser() {
return { id: 1, name: 'Alice' };
}
type UserReturn = ReturnType<typeof getUser>;
// { id: number; name: string; }
class Person { name = 'Bob'; }
type PersonInstance = InstanceType<typeof Person>;
// Person
4. 工具类型的实现思路
常见实现方式有三类:
-
映射类型 :
Record
/Partial
/Pick
/Omit
/Readonly
/Required
-
条件类型 :
Exclude
/Extract
/NonNullable
-
infer
推导 :ReturnType
/InstanceType
5. 最佳实践与注意事项
- 避免过度嵌套 :
Partial<Required<Pick<...>>>
会让类型难以阅读,尽量保持扁平。 - 业务分层使用 :比如 DTO 层用
Omit
去掉数据库 ID,前端表单用Partial
允许字段可选。 - 结合自定义类型:内置工具类型不够时,可以通过条件类型 + 映射类型组合,写出自己的工具类型。
参考更多资料: