讲解
TypeScript 内置了一批基于泛型和映射类型实现的工具类型,覆盖日常八成的类型变换需求,不用手写。按用途分四组。结构变换:Partial(所有属性变可选,更新接口的入参标配)、Required(全部变必填)、Readonly(全部只读)、Pick<T, K>(挑出部分属性)、Omit<T, K>(排除部分属性,派生「新建时不含 id」这类类型最顺手)、Record<K, V>(构造键为 K、值为 V 的对象类型)。
函数与返回值:ReturnType 拿函数返回值类型、Parameters 拿参数元组——给别人的函数写包装器时不用重复声明签名。字符串字面量变换:Uppercase、Lowercase、Capitalize、Uncapitalize,配合模板字面量类型可以做事件名推导(on + Capitalize)。联合处理:Exclude<U, X>(从联合中排除)、Extract<U, X>(提取匹配成员)、NonNullable(去掉 null 和 undefined)。
这些工具类型不是黑魔法,每一个都能用映射类型(下一章)手写出来。建议的策略是:先用熟内置的,遇到组合场景(如「深层 Partial」「把某个键变可选」)再读映射类型一章自己造。切忌为炫技嵌套五六层工具类型——类型是给同事看的,可读性优先。
示例
Partial 与 Omit 的典型业务场景:
interface User {
id: number;
name: string;
email: string;
age: number;
}
type CreateUserInput = Omit<User, "id">; // 新建时还没有 id
type UpdateUserInput = Partial<CreateUserInput>; // 更新时全可选
function updateUser(id: number, patch: UpdateUserInput): string {
const fields = Object.keys(patch).join("、") || "(无修改)";
return `用户 ${id} 更新了:${fields}`;
}
const input: CreateUserInput = { name: "小林", email: "lin@example.com", age: 30 };
console.log(input.name, updateUser(1, { age: 31 }));
Pick 与 Record:
interface Article {
id: number;
title: string;
body: string;
views: number;
}
type ArticleCard = Pick<Article, "id" | "title" | "views">;
type ViewCounter = Record<string, number>;
const card: ArticleCard = { id: 1, title: "工具类型", views: 1024 };
const counter: ViewCounter = { typescript: 3, docker: 1 };
console.log(`${card.title}(${card.views} 次阅读)`, counter);
ReturnType、Parameters 与 NonNullable:
function findUser(id: number): { id: number; name: string } | null {
return id > 0 ? { id, name: "小林" } : null;
}
type FoundUser = NonNullable<ReturnType<typeof findUser>>; // 去掉 null
type FindUserArgs = Parameters<typeof findUser>; // [id: number]
function logFound(...args: FindUserArgs): void {
const user: FoundUser | null = findUser(...args);
if (user !== null) {
console.log(`找到:${user.name}`);
}
}
logFound(1);
常见坑
- Partial 嵌套失效:Partial 只作用于第一层,嵌套对象内部仍然必填;深变换要自己写映射类型或引库。
- Omit 的键不检查存在性:Omit<User, "naem"> 拼错也不报错(Omit 的键约束是宽松版本)——键名务必从 keyof 里选。
- Record<string, T> 滥用:键集合已知时用具体键的 Record<"a" | "b", T>,能换来多余属性检查和补全。
- 把工具类型当业务建模:领域模型该用 interface 清楚声明,工具类型适合「从模型派生视图/入参」,别反过来。
小结
Partial/Required/Readonly/Pick/Omit/Record 处理结构;ReturnType/Parameters 跟随函数;Exclude/Extract/NonNullable 处理联合。下一章揭开它们的实现原理:映射类型。