TypeScript 工具类型:内置工具类型详解
这一篇详细解析 TypeScript 内置的工具类型:从基础的 Partial、Required,
到复杂的条件类型工具,再到如何自定义工具类型。
基础工具类型
Partial
将类型 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
Required
将类型 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}
Readonly
将类型 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
Pick<T, K>
从类型 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}
Omit<T, K>
从类型 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}
Record<K, T>
创建一个对象类型,键的类型是 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};
条件类型工具
Exclude<T, U>
从类型 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>;
Extract<T, U>
从类型 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
NonNullable
从类型 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}
函数类型工具
Parameters
提取函数类型 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}
ReturnType
提取函数类型 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>
ConstructorParameters
提取构造函数类型 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}
InstanceType
提取构造函数类型 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}
字符串操作工具类型
Uppercase<S>
将字符串类型 S 转换为大写:
1type T0 = Uppercase<'hello'>;
2// 'HELLO'
3
4type T1 = Uppercase<'hello' | 'world'>;
5// 'HELLO' | 'WORLD'
Lowercase<S>
将字符串类型 S 转换为小写:
1type T0 = Lowercase<'HELLO'>;
2// 'hello'
Capitalize<S>
将字符串类型 S 的首字母转换为大写:
1type T0 = Capitalize<'hello'>;
2// 'Hello'
Uncapitalize<S>
将字符串类型 S 的首字母转换为小写:
1type T0 = Uncapitalize<'Hello'>;
2// 'hello'
自定义工具类型
深度 Partial
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// }
深度 Required
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// }
深度 Readonly
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
Promise 类型提取
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' }
实际应用场景
API 响应类型转换
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 和配置
- 创建更灵活、可复用的类型系统
- 减少类型定义的冗余代码
在实际开发中,应该:
- 优先使用内置工具类型,而不是手动定义
- 根据需求组合多个工具类型
- 创建自定义工具类型来满足特定需求
- 理解工具类型的实现原理,以便更好地使用