TypeScript 工具类型组合技巧:Partial、Pick、Omit 高级用法
每天都在用 Partial、Pick、Omit,但你知道它们还能组合起来用吗?花 5 分钟读完本文,带你从“知道名字”进阶到“真正会用”。
先看速查表
| 工具类型 | 作用 | 使用频率 |
|---|---|---|
| Partial<T> | 所有属性变可选 | 极高 |
| Required<T> | 所有属性变必填 | 高 |
| Pick<T, K> | 只保留指定属性 | 极高 |
| Omit<T, K> | 排除指定属性 | 极高 |
| Record<K, V> | 构建键值对类型 | 极高 |
技巧一:Partial — 更新接口时的救星
场景:编辑用户信息时,只传需要修改的字段,不必传全部。
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 老写法:手动写一堆可选属性
interface UpdateUser {
name?: string;
email?: string;
age?: number;
}
// 新写法:一行搞定
type UpdateUser = Partial<Omit<User, 'id'>>;
function updateUser(id: number, data: UpdateUser) {
// data 中的所有字段都是可选的,只传要修改的字段即可
console.log('更新用户', id, data);
}
updateUser(1, { name: '张三' }); // 只改名字
updateUser(1, { email: 'a@b.com' }); // 只改邮箱
updateUser(1, { name: '李四', age: 25 }); // 同时改多个说明:Partial<Omit<User, 'id'>> — 先排除 id,再让剩余字段全部可选,一行代码替代手写接口。
技巧二:Pick 与 Omit — 精准提取或排除字段
场景:列表页只需要 id 和 name,详情页才需要全部字段。
interface Article {
id: number;
title: string;
content: string;
author: string;
createdAt: string;
tags: string[];
}
// 列表卡片:只要 id、title、author、createdAt
type ArticleCard = Pick<Article, 'id' | 'title' | 'author' | 'createdAt'>;
// 编辑表单:排除 id(不可修改)和 createdAt(自动生成)
type ArticleForm = Omit<Article, 'id' | 'createdAt'>;
const card: ArticleCard = {
id: 1,
title: 'TypeScript 工具类型',
author: '出炉君',
createdAt: '2026-04-13',
};
// card.content // 报错,Pick 已经排除了 content说明:Pick 和 Omit 怎么选?要保留的字段少就用 Pick,要排除的字段少就用 Omit,哪个打字少就用哪个。
技巧三:Record — 告别 { [key: string]: any }
场景:使用字符串枚举作为 key,构建类型安全的映射表。
type Status = 'pending' | 'success' | 'error' | 'loading';
// 老写法:key 类型太宽泛,容易写错
const statusMap: { [key: string]: string } = {
pending: '等待中',
success: '成功',
};
// 新写法:key 类型安全,不能乱填,且必须补全所有枚举值
const statusMap: Record<Status, string> = {
pending: '等待中',
success: '成功',
error: '失败',
loading: '加载中',
// 少写任何一个 key 都会报错,完整性有保证
};
// 更复杂的配置:每个状态对应标签和颜色
const statusConfig: Record<Status, { label: string; color: string }> = {
pending: { label: '等待中', color: '#faad14' },
success: { label: '成功', color: '#52c41a' },
error: { label: '失败', color: '#ff4d4f' },
loading: { label: '加载中', color: '#1890ff' },
};
console.log(statusConfig['success']); // { label: '成功', color: '#52c41a' }说明:用 Record<枚举类型, V> 代替 { [key: string]: V },TypeScript 会强制你覆盖所有枚举值,避免遗漏。
技巧四:组合技 — Required + Partial 分区控制
场景:部分字段必填,部分字段可选,精确控制每个字段的必填/可选状态。
interface Config {
host: string;
port: number;
timeout?: number;
retries?: number;
debug?: boolean;
}
// 必填核心配置(host、port),其余调优配置均可选
type ServerConfig = Required<Pick<Config, 'host' | 'port'>> & Partial<Omit<Config, 'host' | 'port'>>;
// 正确:host 和 port 必填,其他字段可选
const config: ServerConfig = {
host: 'localhost',
port: 3000,
debug: true, // 可选
};
// 报错:缺少必填的 port
// const config2: ServerConfig = { host: 'localhost' };说明:Required<Pick<...>> + Partial<Omit<...>> 的组合,能精准控制哪些字段必填、哪些可选,比手动添加 ? 优雅得多。
技巧五:Readonly — 防止数据被意外修改
interface AppState {
user: { id: number; name: string };
theme: 'light' | 'dark';
locale: string;
}
// 整个状态变为只读,防止直接修改
type ReadonlyState = Readonly<AppState>;
const state: ReadonlyState = {
user: { id: 1, name: '张三' },
theme: 'light',
locale: 'zh-CN',
};
// state.theme = 'dark'; // 报错:因为 theme 是只读属性最容易踩的坑:Omit 的 key 拼错不会报错
interface User {
id: number;
name: string;
}
// 拼错了 key,TypeScript 竟然不报错
type WrongOmit = Omit<User, 'namee'>; // 结果等同于 User,namee 不存在但被静默忽略
// 安全写法:加 & keyof T 约束,让 TypeScript 检查 key 是否有效
type SafeOmit<T, K extends keyof T> = Omit<T, K>;
type CorrectOmit = SafeOmit<User, 'name'>; // 拼错 key 会立即报错组合速查
| 需求 | 写法 |
|---|---|
| 更新接口(排除 id,其余可选) | Partial<Omit<T, 'id'>> |
| 列表卡片(只要几个字段) | Pick<T, 'id' | 'name'> |
| 枚举映射表 | Record<Status, Config> |
| 必填+可选分区 | Required<Pick<...>> & Partial<Omit<...>> |
| 防止状态被修改 | Readonly<State> |
完整可运行 Demo
将下面代码复制到 TypeScript Playground 或本地 .ts 文件中运行:
// 1. Partial + Omit:更新接口
interface User {
id: number;
name: string;
email: string;
}
type UpdateUser = Partial<Omit<User, 'id'>>;
const update: UpdateUser = { name: '张三' }; // 正确
// 2. Record:枚举映射表
type Status = 'ok' | 'fail' | 'loading';
const statusLabel: Record<Status, string> = {
ok: '成功',
fail: '失败',
loading: '加载中',
};
console.log(statusLabel['ok']); // 成功
// 3. Required + Pick + Partial 组合
interface Config {
host: string;
port: number;
debug?: boolean;
}
type ServerConfig = Required<Pick<Config, 'host' | 'port'>> & Partial<Pick<Config, 'debug'>>;
const cfg: ServerConfig = { host: 'localhost', port: 3000 }; // 正确
// 4. Pick 提取列表字段
interface Article {
id: number;
title: string;
content: string;
author: string;
}
type ArticleCard = Pick<Article, 'id' | 'title' | 'author'>;
const card: ArticleCard = { id: 1, title: 'TS 工具类型', author: '出炉君' };
console.log(card);
// 5. Readonly 防止状态修改
const state: Readonly<{ count: number }> = { count: 0 };
// state.count = 1; // 报错,只读属性
console.log(state.count); // 0
console.log('所有工具类型演示通过!');希望这 5 个组合技能让你的 TypeScript 类型定义更简洁、更安全。
本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!