Skip to content
第 105 / 250 章前端⏱ 10 分钟阅读

第 105 章:工具类型(Utility Types)

学习目标

  • 掌握 TS 内置工具类型的完整集合
  • 学会查阅和组合工具类型
  • 理解工具类型的实现原理
  • 能在项目中灵活应用工具类型

一、属性修饰类工具类型

1.1 Partial<T>

T 的所有属性变为可选

typescript
type Partial<T> = {
  [K in keyof T]?: T[K];
};

interface User {
  id: number;
  name: string;
  email: string;
}

// 场景:更新时只传部分字段
function updateUser(id: number, data: Partial<User>) {
  // ...
}

updateUser(1, { name: 'Tom' });  // ✅

实战 - 深 Partial:

typescript
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

interface Nested {
  a: number;
  b: { c: string; d: { e: boolean } };
}

type PartialNested = DeepPartial<Nested>;
// { a?: number; b?: { c?: string; d?: { e?: boolean } } }

1.2 Required<T>

T 的所有属性变为必填

typescript
type Required<T> = {
  [K in keyof T]-?: T[K];
};

interface Config {
  host?: string;
  port?: number;
}

const config: Required<Config> = {
  host: 'localhost',  // 现在必填
  port: 3000          // 现在必填
};

1.3 Readonly<T>

T 的所有属性变为只读

typescript
type Readonly<T> = {
  readonly [K in keyof T]: T[K];
};

const user: Readonly<User> = { id: 1, name: 'Tom', email: 'a@b.c' };
user.name = 'Jerry';  // ❌ Cannot assign to 'name'

1.4 Mutable<T>(自定义,移除 readonly)

typescript
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

二、结构选择类工具类型

2.1 Pick<T, K>

T挑选指定键。

typescript
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string }

2.2 Omit<T, K>

T排除指定键。

typescript
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

type CreateUserDTO = Omit<User, 'id'>;
// { name: string; email: string }

2.3 Record<K, V>

构造键值对对象类型。

typescript
type Record<K extends keyof any, V> = {
  [P in K]: V;
};

// 用法 1:已知键集合
type Page = Record<'home' | 'about' | 'contact', { title: string }>;
// { home: { title: string }; about: { title: string }; contact: { title: string } }

// 用法 2:动态键
type StringMap = Record<string, number>;
// { [key: string]: number }

// 用法 3:对象类型化
type UserRoles = Record<User['role'], string[]>;
// { admin: string[]; user: string[] }

2.4 Exclude<T, U>

T 中排除可赋值给 U的类型。

typescript
type Exclude<T, U> = T extends U ? never : T;

// 排除具体成员
type T1 = Exclude<'a' | 'b' | 'c', 'a'>;  // 'b' | 'c'

// 排除类型
type T2 = Exclude<string | number | boolean, number | boolean>;  // string

2.5 Extract<T, U>

T提取可赋值给 U的类型。

typescript
type Extract<T, U> = T extends U ? T : never;

type T1 = Extract<'a' | 'b' | 'c', 'a' | 'b'>;  // 'a' | 'b'

// 提取函数类型
type Mixed = string | number | (() => void) | boolean;
type OnlyFunctions = Extract<Mixed, Function>;  // () => void

三、类型操作类工具类型

3.1 NonNullable<T>

T 中排除 nullundefined

typescript
type NonNullable<T> = T extends null | undefined ? never : T;

type T1 = NonNullable<string | null>;       // string
type T2 = NonNullable<number | undefined>;   // number
type T3 = NonNullable<null | undefined>;    // never

3.2 ReturnType<T>

获取函数 T返回值类型

typescript
type ReturnType<T extends (...args: any) => any> =
  T extends (...args: any) => infer R ? R : any;

function fetchUser(): User { /* ... */ }
type R = ReturnType<typeof fetchUser>;  // User

// 实战:从已有函数派生类型
async function getUser() {
  return await fetchUser();
}
type UserType = ReturnType<typeof getUser>;  // Promise<User>

3.3 Parameters<T>

获取函数 T参数类型(元组)。

typescript
type Parameters<T extends (...args: any) => any> =
  T extends (...args: infer P) => any ? P : never;

function fn(a: string, b: number, c: boolean) { }
type P = Parameters<typeof fn>;  // [a: string, b: number, c: boolean]

3.4 InstanceType<T>

获取构造函数 T实例类型

typescript
type InstanceType<T extends new (...args: any) => any> =
  T extends new (...args: any) => infer R ? R : any;

class User { name = 'Tom' }
type U = InstanceType<typeof User>;  // User

// 实战:从类构造函数派生
function createInstance<T extends new (...args: any) => any>(
  ctor: T,
  ...args: ConstructorParameters<T>
): InstanceType<T> {
  return new ctor(...args);
}

3.5 ThisParameterType<T> / OmitThisParameter<T>

typescript
function greet(this: User, name: string) {
  return `Hello, ${this.name}, I'm ${name}`;
}

type T1 = ThisParameterType<typeof greet>;  // User
type T2 = OmitThisParameter<typeof greet>;  // (name: string) => string

四、字符串操作类(TS 4.1+)

typescript
type Uppercase<S extends string> = intrinsic;
type Lowercase<S extends string> = intrinsic;
type Capitalize<S extends string> = intrinsic;
type Uncapitalize<S extends string> = intrinsic;

type A = Uppercase<'hello'>;     // 'HELLO'
type B = Lowercase<'HELLO'>;     // 'hello'
type C = Capitalize<'hello'>;    // 'Hello'
type D = Uncapitalize<'Hello'>;  // 'hello'

// 实战:事件名转换
type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`;
// 'onClick' | 'onFocus' | 'onBlur'

五、Promise / 异步类(TS 4.5+)

5.1 Awaited<T>

递归解开 Promise

typescript
type Awaited<T> =
  T extends null | undefined ? T :
  T extends object & { then(onfulfilled: infer F, ...args: infer _) } ?
    F extends ((value: infer V, ...args: infer _) => any) ?
      Awaited<V> : never : T;

type T1 = Awaited<Promise<string>>;               // string
type T2 = Awaited<Promise<Promise<number>>>;      // number(递归解开)

六、自定义工具类型

6.1 递归 Partial

typescript
type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

// 用法
interface Nested { a: { b: { c: number } } }
type P = DeepPartial<Nested>;
// { a?: { b?: { c?: number } } }

6.2 递归 Readonly

typescript
type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

6.3 递归 Required

typescript
type DeepRequired<T> = T extends object
  ? { [K in keyof T]-?: DeepRequired<T[K]> }
  : T;

6.4 过滤 nullable 字段

typescript
type NonNullableFields<T> = {
  [K in keyof T as T[K] extends null | undefined ? never : K]: T[K];
};

interface User {
  id: number;
  name: string | null;
  email: string | undefined;
  age: number;
}

type Clean = NonNullableFields<User>;
// { id: number; age: number }

6.5 函数参数提取

typescript
// 提取最后一个参数
type LastParameter<T extends (...args: any[]) => any> =
  T extends (...args: infer P) => any
    ? P extends [...any[], infer L]
      ? L
      : never
    : never;

function fn(a: string, b: number, c: boolean) { }
type L = LastParameter<typeof fn>;  // boolean

6.6 异步函数提取

typescript
type AsyncReturnType<T extends (...args: any) => Promise<any>> =
  T extends (...args: any) => Promise<infer R> ? R : never;

async function fetchUser(): Promise<User> { /* ... */ }
type T = AsyncReturnType<typeof fetchUser>;  // User

6.7 联合转交叉

typescript
type UnionToIntersection<U> =
  (U extends any ? (x: U) => void : never) extends (x: infer I) => void
    ? I
    : never;

type T = UnionToIntersection<{ a: string } | { b: number }>;
// { a: string } & { b: number }

七、工具类型组合实战

7.1 CRUD 类型模板

typescript
interface BaseEntity {
  id: number;
  createdAt: Date;
  updatedAt: Date;
}

// 创建:不需要 id 和时间戳
type CreateDTO<T> = Omit<T, 'id' | 'createdAt' | 'updatedAt'>;

// 更新:所有字段可选,不需要 id
type UpdateDTO<T> = Partial<Omit<T, 'id'>>;

// 详情:必带 id
type DetailDTO<T> = T;

// 列表查询
type ListQuery<T> = Partial<T> & {
  page: number;
  pageSize: number;
};

// 列表响应
type ListResponse<T> = {
  items: T[];
  total: number;
  page: number;
  pageSize: number;
};

// 使用
interface Article extends BaseEntity {
  title: string;
  content: string;
  authorId: number;
}

type CreateArticleDTO = CreateDTO<Article>;
type UpdateArticleDTO = UpdateDTO<Article>;
type ListArticleQuery = ListQuery<Article>;
type ArticleListResponse = ListResponse<Article>;

7.2 API 响应统一封装

typescript
type ApiSuccess<T> = {
  code: 0;
  message: 'ok';
  data: T;
};

type ApiError = {
  code: number;
  message: string;
};

type ApiResponse<T> = ApiSuccess<T> | ApiError;

// 分页数据
type PagedApiResponse<T> = ApiSuccess<{
  items: T[];
  total: number;
}>;

// 单条数据
type ItemApiResponse<T> = ApiSuccess<T>;

7.3 表单状态管理

typescript
type FieldState<T> = {
  value: T;
  error?: string;
  touched: boolean;
  dirty: boolean;
};

type FormState<T> = {
  [K in keyof T]: FieldState<T[K]>;
};

interface LoginForm {
  username: string;
  password: string;
}

type LoginFormState = FormState<LoginForm>;
// {
//   username: FieldState<string>;
//   password: FieldState<string>;
// }

7.4 Redux Action / Reducer 类型

typescript
// Action 类型
type Action<T extends string, P = void> = P extends void
  ? { type: T }
  : { type: T; payload: P };

// Reducer 函数
type Reducer<S, A extends { type: string }> = (state: S, action: A) => S;

// State 类型
type StateOf<R extends Reducer<any, any>> = R extends Reducer<infer S, any> ? S : never;
type ActionOf<R extends Reducer<any, any>> = R extends Reducer<any, infer A> ? A : never;

7.5 安全的 Object.keys

typescript
// Object.keys 返回 string[],丢失了具体键名
const obj = { a: 1, b: 2 };
Object.keys(obj).forEach((key) => {
  console.log(obj[key as keyof typeof obj]);  // 需要断言
});

// 类型安全的 keys
function typedKeys<T extends object>(obj: T): Array<keyof T> {
  return Object.keys(obj) as Array<keyof T>;
}

typedKeys(obj).forEach((key) => {
  console.log(obj[key]);  // ✅ 不需要断言
});

八、工具类型速查表

工具类型作用示例
Partial<T>所有字段可选Partial<User>
Required<T>所有字段必填Required<Config>
Readonly<T>所有字段只读Readonly<User>
Pick<T, K>挑选字段Pick<User, 'id' | 'name'>
Omit<T, K>排除字段Omit<User, 'id'>
Record<K, V>构造字典Record<string, number>
Exclude<T, U>排除联合成员Exclude<'a'|'b', 'a'>
Extract<T, U>提取联合成员Extract<string|number, string>
NonNullable<T>排除 null/undefinedNonNullable<string|null>
ReturnType<T>函数返回值ReturnType<typeof fn>
Parameters<T>函数参数Parameters<typeof fn>
InstanceType<T>类实例类型InstanceType<typeof User>
Awaited<T>Promise 内部类型Awaited<Promise<string>>
Uppercase<S>转大写Uppercase<'hi'>
Lowercase<S>转小写Lowercase<'HI'>
Capitalize<S>首字母大写Capitalize<'hi'>
Uncapitalize<S>首字母小写Uncapitalize<'Hi'>

九、常见错误

9.1 Partial 不递归

typescript
interface Nested {
  user: { name: string; age: number };
}

type P = Partial<Nested>;
// { user?: { name: string; age: number } }
// 只有 user 变成可选,内部 user 的字段仍然必填

// 想要嵌套可选:用 DeepPartial

9.2 Omit 和 Pick 顺序

typescript
// 错误用法:K 不在 T 中
type Bad = Omit<User, 'address'>;  // User 中没有 address,会报错
type Good = Omit<User, 'email'>;   // ✅

9.3 ReturnType 接受类

typescript
class User {
  greet() { return 'hello'; }
}

type R = ReturnType<typeof User>;  // ❌ 不能对类用

// 应该用 InstanceType
type U = InstanceType<typeof User>;  // User
type Greet = ReturnType<U['greet']>;  // string

十、本章小结

类别工具类型
属性修饰Partial / Required / Readonly / Mutable
结构选择Pick / Omit / Record
联合操作Exclude / Extract / NonNullable
函数提取ReturnType / Parameters / ThisParameterType / OmitThisParameter
类操作InstanceType / ConstructorParameters
PromiseAwaited
字符串Uppercase / Lowercase / Capitalize / Uncapitalize
自定义DeepPartial / DeepReadonly / NonNullableFields

动手练习

  1. DeepPartial:实现递归 Partial,让嵌套对象也变可选
  2. PickByType:写一个 PickByType<T, V>,只保留值类型为 V 的字段
  3. Mutable:写一个 Mutable<T>,移除所有 readonly
  4. AsyncReturnType:写一个 AsyncReturnType<T>,提取异步函数 resolve 的值类型

推荐阅读


下一章第 106 章:模块系统(Modules)

本站基于 VitePress 构建 · 由 Codebook 团队维护