TypeScript 工具类型

TS 内置了一批"工具类型"(Utility Types),它们是对类型进行变换的函数——传入一个类型,返回一个新类型。日常开发中,PartialPickOmitRecord 这几个几乎天天用。这一章把最高频的几个讲透。

1. Partial / Required / Readonly

这三个最简单:Partial<T> 把 T 的所有属性变可选,Required<T> 全变必填,Readonly<T> 全变只读。其中 Partial 用得最多——做"部分更新"时简直是标配:

// Partial<T>:把 T 的所有属性变成可选
interface User {
  id: number;
  name: string;
  age: number;
}

// 更新用户信息时,通常只传部分字段
function updateUser(id: number, patch: Partial<User>): void {
  // patch 的每个属性都是可选的
  console.log(`更新用户 ${id}`, patch);
}
updateUser(1, { name: "新名字" });          // ✅ 只传 name
updateUser(2, { name: "a", age: 21 });      // ✅ 传部分
// updateUser(3, { extra: 1 });             // ❌ extra 不在 User 里

// 相关:Required<T> 全部变必填;Readonly<T> 全部变只读
type RequiredUser = Required<User>;
type ReadonlyUser = Readonly<User>;

2. Pick / Omit(挑选与排除)

这俩互为反面:Pick<T, K> 从 T 里几个字段,Omit<T, K> 从 T 里排除几个字段。它们是"复用已有类型派生新类型"的核心工具:

// Pick<T, Keys>:从 T 里挑几个字段
interface Article {
  title: string;
  content: string;
  author: string;
  tags: string[];
}

type ArticlePreview = Pick<Article, "title" | "author">;
// 等价于 { title: string; author: string }

const preview: ArticlePreview = {
  title: "标题",
  author: "小明",
};

// Omit<T, Keys>:从 T 里排除几个字段(和 Pick 相反)
type CreateArticleDTO = Omit<Article, "author">;
// 等价于 { title: string; content: string; tags: string[] }

// Pick 挑选,Omit 排除,二者互补

3. Record(构造键值对类型)

Record<K, V> 构造一个"键类型为 K、值类型为 V"的对象类型。它非常适合描述映射表、配置表、权限表

// Record<K, V>:构造一个"键为 K、值为 V"的对象类型
type Role = "admin" | "editor" | "viewer";

// 角色对应的权限列表
const permissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
};

// 常用于"映射表/配置表"
const labelMap: Record<number, string> = {
  1: "待处理",
  2: "进行中",
  3: "已完成",
};

Record 配合字面量联合类型(如 Role),TS 会强制你为每个键都提供值,漏一个就报错——非常安全。

4. ReturnType / Parameters(提取函数信息)

这俩从已有的函数提取返回值类型或参数类型,避免重复定义。在复用第三方函数的类型时特别有用:

// ReturnType<T>:取函数 T 的返回值类型
function getUser() {
  return { id: 1, name: "小明" };
}

type User = ReturnType<typeof getUser>;
// 等价于 { id: number; name: string }

// Parameters<T>:取函数的参数类型(元组)
function add(a: number, b: string): void {}
type AddArgs = Parameters<typeof add>;   // [number, string]

// 这两个在写"复用函数返回值/参数类型"时极其常用

5. 实战:从后端模型派生表单类型

把工具类型组合起来,解决一个真实问题:一个完整的产品模型,怎么派生出"创建表单"和"更新表单"两种类型?

// 实战:把后端返回的"完整模型"变成"创建表单"的 DTO
interface Product {
  id: number;              // 后端生成,创建时不传
  name: string;
  price: number;
  createdAt: string;       // 后端生成
  updatedAt: string;       // 后端生成
}

// 创建产品的表单只需要用户能填的字段
type ProductForm = Omit<Product, "id" | "createdAt" | "updatedAt">;
// 等价于 { name: string; price: number }

// 编辑产品的表单:所有字段都可选
type ProductPatch = Partial<ProductForm>;

function create(p: ProductForm): void { /* ... */ }
function update(id: number, p: ProductPatch): void { /* ... */ }

这种"从单一数据源派生多种类型"的写法,是 TS 工程化的精髓——改一处 Product,所有派生类型自动更新,绝不重复定义。

6. 其他常用工具类型

除了上面几个,TS 还内置了这些(不细讲,知道有就行):

小结

工具类型是"对类型做变换的函数"。最该记牢的是这五个:Partial(全可选)、Pick(挑字段)、Omit(排除字段)、Record(键值对)、ReturnType(取返回值)。它们让你能从已有类型派生新类型,避免重复定义。下一篇讲类型断言与类型守卫

← 上一篇 TypeScript 模块

下一篇 TypeScript 类型断言与守卫

✈️💬