第 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>; // string2.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 中排除 null 和 undefined。
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>; // never3.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>; // boolean6.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>; // User6.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/undefined | NonNullable<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 的字段仍然必填
// 想要嵌套可选:用 DeepPartial9.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 |
| Promise | Awaited |
| 字符串 | Uppercase / Lowercase / Capitalize / Uncapitalize |
| 自定义 | DeepPartial / DeepReadonly / NonNullableFields |
动手练习
- DeepPartial:实现递归 Partial,让嵌套对象也变可选
- PickByType:写一个
PickByType<T, V>,只保留值类型为 V 的字段 - Mutable:写一个
Mutable<T>,移除所有 readonly - AsyncReturnType:写一个
AsyncReturnType<T>,提取异步函数 resolve 的值类型
推荐阅读
- 📖 TypeScript Handbook - Utility Types — 官方工具类型清单
- 📖 TypeScript Handbook - Template Literal Types — 模板字面量类型
- 🌐 utility-types — 社区扩展工具类型库
下一章:第 106 章:模块系统(Modules) →