TypeScript 工具类型:内置工具类型详解

这一篇详细解析 TypeScript 内置的工具类型:从基础的 Partial、Required,
到复杂的条件类型工具,再到如何自定义工具类型。

将类型 T 的所有属性变为可选:

 1interface User {
 2  name: string;
 3  age: number;
 4  email: string;
 5}
 6
 7type PartialUser = Partial<User>;
 8// { name?: string; age?: number; email?: string; }
 9
10// 实现原理
11type MyPartial<T> = {
12  [P in keyof T]?: T[P];
13};
14
15// 实际应用:更新操作
16function updateUser(user: User, updates: Partial<User>): User {
17  return { ...user, ...updates };
18}
19
20const user: User = { name: 'John', age: 30, email: 'john@example.com' };
21updateUser(user, { age: 31 }); // 只更新 age

将类型 T 的所有属性变为必需:

 1interface User {
 2  name?: string;
 3  age?: number;
 4  email?: string;
 5}
 6
 7type RequiredUser = Required<User>;
 8// { name: string; age: number; email: string; }
 9
10// 实现原理
11type MyRequired<T> = {
12  [P in keyof T]-?: T[P];
13};
14
15// 实际应用:确保所有属性都存在
16function createUser(user: Required<User>): void {
17  // 这里可以确保所有属性都存在
18  console.log(user.name, user.age, user.email);
19}

将类型 T 的所有属性变为只读:

 1interface User {
 2  name: string;
 3  age: number;
 4}
 5
 6type ReadonlyUser = Readonly<User>;
 7// { readonly name: string; readonly age: number; }
 8
 9// 实现原理
10type MyReadonly<T> = {
11  readonly [P in keyof T]: T[P];
12};
13
14// 实际应用:不可变数据
15const readonlyUser: ReadonlyUser = { name: 'John', age: 30 };
16// readonlyUser.name = 'Jane'; // 错误:Cannot assign to 'name' because it is a read-only property

从类型 T 中选择指定的属性 K:

 1interface User {
 2  name: string;
 3  age: number;
 4  email: string;
 5  password: string;
 6}
 7
 8type PublicUser = Pick<User, 'name' | 'age' | 'email'>;
 9// { name: string; age: number; email: string; }
10
11// 实现原理
12type MyPick<T, K extends keyof T> = {
13  [P in K]: T[P];
14};
15
16// 实际应用:创建公开的用户信息
17function getPublicUser(user: User): PublicUser {
18  const { password, ...publicUser } = user;
19  return publicUser;
20}

从类型 T 中排除指定的属性 K:

 1interface User {
 2  name: string;
 3  age: number;
 4  email: string;
 5  password: string;
 6}
 7
 8type PublicUser = Omit<User, 'password'>;
 9// { name: string; age: number; email: string; }
10
11// 实现原理
12type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
13
14// 实际应用:排除敏感信息
15function sanitizeUser(user: User): Omit<User, 'password'> {
16  const { password, ...sanitized } = user;
17  return sanitized;
18}

创建一个对象类型,键的类型是 K,值的类型是 T:

 1type UserRoles = 'admin' | 'user' | 'guest';
 2type Permissions = string[];
 3
 4type RolePermissions = Record<UserRoles, Permissions>;
 5// {
 6//   admin: string[];
 7//   user: string[];
 8//   guest: string[];
 9// }
10
11// 实现原理
12type MyRecord<K extends keyof any, T> = {
13  [P in K]: T;
14};
15
16// 实际应用:配置对象
17const permissions: RolePermissions = {
18  admin: ['read', 'write', 'delete'],
19  user: ['read', 'write'],
20  guest: ['read']
21};

从类型 T 中排除可以赋值给 U 的类型:

 1type T0 = Exclude<'a' | 'b' | 'c', 'a'>;
 2// 'b' | 'c'
 3
 4type T1 = Exclude<string | number | boolean, number>;
 5// string | boolean
 6
 7type T2 = Exclude<string | number | (() => void), Function>;
 8// string | number
 9
10// 实现原理
11type MyExclude<T, U> = T extends U ? never : T;
12
13// 实际应用:过滤类型
14type NonNullable<T> = Exclude<T, null | undefined>;
15type StringOrNumber = Exclude<string | number | boolean, boolean>;

从类型 T 中提取可以赋值给 U 的类型:

 1type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;
 2// 'a'
 3
 4type T1 = Extract<string | number | boolean, number>;
 5// number
 6
 7// 实现原理
 8type MyExtract<T, U> = T extends U ? T : never;
 9
10// 实际应用:提取特定类型
11type FunctionTypes = Extract<string | number | (() => void), Function>;
12// () => void

从类型 T 中排除 null 和 undefined:

 1type T0 = NonNullable<string | number | undefined>;
 2// string | number
 3
 4type T1 = NonNullable<string[] | null | undefined>;
 5// string[]
 6
 7// 实现原理
 8type MyNonNullable<T> = Exclude<T, null | undefined>;
 9
10// 实际应用:确保值不为空
11function processValue<T>(value: NonNullable<T>): void {
12  // value 不会是 null 或 undefined
13}

提取函数类型 T 的参数类型元组:

 1type Func = (a: string, b: number) => boolean;
 2type Args = Parameters<Func>;
 3// [string, number]
 4
 5// 实现原理
 6type MyParameters<T extends (...args: any) => any> = 
 7  T extends (...args: infer P) => any ? P : never;
 8
 9// 实际应用:函数包装
10function wrapFunction<T extends (...args: any[]) => any>(
11  fn: T
12): (...args: Parameters<T>) => ReturnType<T> {
13  return (...args: Parameters<T>) => {
14    console.log('Calling function with args:', args);
15    return fn(...args);
16  };
17}

提取函数类型 T 的返回类型:

 1type Func = () => string;
 2type Return = ReturnType<Func>;
 3// string
 4
 5// 实现原理
 6type MyReturnType<T extends (...args: any) => any> = 
 7  T extends (...args: any) => infer R ? R : any;
 8
 9// 实际应用:异步函数返回类型
10async function fetchUser(id: string): Promise<User> {
11  // ...
12}
13
14type UserPromise = ReturnType<typeof fetchUser>;
15// Promise<User>

提取构造函数类型 T 的参数类型元组:

 1class User {
 2  constructor(name: string, age: number) {}
 3}
 4
 5type ConstructorArgs = ConstructorParameters<typeof User>;
 6// [string, number]
 7
 8// 实现原理
 9type MyConstructorParameters<T extends new (...args: any) => any> = 
10  T extends new (...args: infer P) => any ? P : never;
11
12// 实际应用:工厂函数
13function createInstance<T extends new (...args: any[]) => any>(
14  Constructor: T,
15  ...args: ConstructorParameters<T>
16): InstanceType<T> {
17  return new Constructor(...args);
18}

提取构造函数类型 T 的实例类型:

 1class User {
 2  name: string;
 3  age: number;
 4}
 5
 6type UserInstance = InstanceType<typeof User>;
 7// User
 8
 9// 实现原理
10type MyInstanceType<T extends new (...args: any) => any> = 
11  T extends new (...args: any) => infer R ? R : any;
12
13// 实际应用:泛型工厂
14function create<T extends new (...args: any[]) => any>(
15  Constructor: T,
16  ...args: ConstructorParameters<T>
17): InstanceType<T> {
18  return new Constructor(...args);
19}

将字符串类型 S 转换为大写:

1type T0 = Uppercase<'hello'>;
2// 'HELLO'
3
4type T1 = Uppercase<'hello' | 'world'>;
5// 'HELLO' | 'WORLD'

将字符串类型 S 转换为小写:

1type T0 = Lowercase<'HELLO'>;
2// 'hello'

将字符串类型 S 的首字母转换为大写:

1type T0 = Capitalize<'hello'>;
2// 'Hello'

将字符串类型 S 的首字母转换为小写:

1type T0 = Uncapitalize<'Hello'>;
2// 'hello'
 1type DeepPartial<T> = {
 2  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
 3};
 4
 5interface User {
 6  name: string;
 7  address: {
 8    street: string;
 9    city: string;
10  };
11}
12
13type PartialUser = DeepPartial<User>;
14// {
15//   name?: string;
16//   address?: {
17//     street?: string;
18//     city?: string;
19//   };
20// }
 1type DeepRequired<T> = {
 2  [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
 3};
 4
 5interface User {
 6  name?: string;
 7  address?: {
 8    street?: string;
 9    city?: string;
10  };
11}
12
13type RequiredUser = DeepRequired<User>;
14// {
15//   name: string;
16//   address: {
17//     street: string;
18//     city: string;
19//   };
20// }
 1type DeepReadonly<T> = {
 2  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
 3};
 4
 5interface User {
 6  name: string;
 7  address: {
 8    street: string;
 9    city: string;
10  };
11}
12
13type ReadonlyUser = DeepReadonly<User>;
14// {
15//   readonly name: string;
16//   readonly address: {
17//     readonly street: string;
18//     readonly city: string;
19//   };
20// }
 1type RequiredKeys<T> = {
 2  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
 3}[keyof T];
 4
 5type OptionalKeys<T> = {
 6  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
 7}[keyof T];
 8
 9type MakeRequired<T, K extends keyof T> = T & {
10  [P in K]-?: T[P];
11};
 1// 提取第一个参数
 2type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any 
 3  ? F 
 4  : never;
 5
 6// 提取最后一个参数
 7type LastParameter<T> = T extends (...args: infer P) => any
 8  ? P extends [...any[], infer L]
 9    ? L
10    : never
11  : never;
1type ArrayElement<T> = T extends (infer U)[] ? U : never;
2
3type Element = ArrayElement<string[]>; // string
4type Element2 = ArrayElement<number[]>; // number
1type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
2
3type Result = Awaited<Promise<Promise<string>>>; // string
1type ValueOf<T> = T[keyof T];
2
3type Values = ValueOf<{ a: string; b: number }>; // string | number
1type Flip<T extends Record<string, any>> = {
2  [K in keyof T as `${T[K]}`]: K;
3};
4
5type Flipped = Flip<{ a: 'x'; b: 'y' }>; // { x: 'a'; y: 'b' }
 1interface ApiResponse<T> {
 2  data: T;
 3  status: number;
 4  message: string;
 5}
 6
 7// 只提取数据部分
 8type ApiData<T> = T extends ApiResponse<infer D> ? D : never;
 9
10// 使用
11type UserResponse = ApiResponse<User>;
12type UserData = ApiData<UserResponse>; // User
 1interface FormFields {
 2  name: string;
 3  email: string;
 4  age: number;
 5}
 6
 7// 所有字段都是可选的,用于创建表单
 8type FormInput = Partial<FormFields>;
 9
10// 所有字段都是必需的,用于提交
11type FormSubmit = Required<FormFields>;
 1interface Config {
 2  api: string;
 3  timeout: number;
 4  retries: number;
 5}
 6
 7// 部分配置更新
 8type ConfigUpdate = Partial<Pick<Config, 'timeout' | 'retries'>>;
 9
10// 只读配置
11type ReadonlyConfig = Readonly<Config>;

TypeScript 的工具类型提供了强大的类型转换能力:

  • 基础工具类型:Partial、Required、Readonly、Pick、Omit、Record
  • 条件类型工具:Exclude、Extract、NonNullable
  • 函数类型工具:Parameters、ReturnType、ConstructorParameters、InstanceType
  • 字符串操作工具:Uppercase、Lowercase、Capitalize、Uncapitalize
  • 自定义工具类型:深度操作、参数提取、类型转换等

掌握这些工具类型,可以:

  • 快速创建新的类型,而不需要重复定义
  • 实现类型安全的 API 和配置
  • 创建更灵活、可复用的类型系统
  • 减少类型定义的冗余代码

在实际开发中,应该:

  • 优先使用内置工具类型,而不是手动定义
  • 根据需求组合多个工具类型
  • 创建自定义工具类型来满足特定需求
  • 理解工具类型的实现原理,以便更好地使用