03.TypeScript 常见泛型工具详解
什么是泛型工具类型?
在 TypeScript 中,泛型工具类型(Generic Utility Types)是一些内置的泛型类型,它们可以帮助我们更方便地操作和转换类型。这些工具类型是 TypeScript 类型系统的重要组成部分,能够让我们以类型安全的方式进行复杂的类型操作。
1. Partial
Partial 将类型 T 的所有属性都变为可选。
1.1 实例
ts
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 使用 Partial 创建一个可选属性的用户类型
function updateUser(id: number, updates: Partial<User>): void {
// 更新用户的部分属性
console.log(`Updating user ${id} with`, updates);
}
// 可以只传递部分属性
updateUser(1, { name: "Alice" });
updateUser(2, { email: "bob@example.com", age: 30 });
1.2 实现
ts
// Partial 的实现原理
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
// 源码
type Partial<T> = {
[P in keyof T]?: T[P];
};
2. Required
Required 将类型 T 的所有属性都变为必填。
2.1 实例
ts
interface UserOptions {
id?: number;
name?: string;
email?: string;
isActive?: boolean;
}
// 使用 Required 确保所有属性都是必需的
function createUser(user: Required<UserOptions>): void {
console.log("Creating user:", user);
}
// 必须提供所有属性
createUser({
id: 1,
name: "Alice",
email: "alice@example.com",
isActive: true
});
2.2 实现
ts
// Required 的实现原理
type MyRequired<T> = {
[P in keyof T]-?: T[P];
};
// 源码
type Required<T> = {
[P in keyof T]-?: T[P];
};
3. Readonly
Readonly 将类型 T 的所有属性都变为只读。
3.1 实例
ts
interface Config {
apiUrl: string;
timeout: number;
retries: number;
}
// 使用 Readonly 创建只读配置
const config: Readonly<Config> = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3
};
// 以下操作会报错,因为属性是只读的
// config.apiUrl = "https://new-api.example.com"; // Error!
3.2 实现
ts
// Readonly 的实现原理
type MyReadonly<T> = {
readonly [P in keyof T]: T[P];
};
// 源码
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
4. Record
Record<K, T> 创建一个对象类型,属性键为 K,属性值为 T。
4.1 实例
ts
// 创建一个映射类型,键为字符串,值为数字
const scores: Record<string, number> = {
"Alice": 95,
"Bob": 87,
"Charlie": 92
};
// 创建一个具有特定键的配置对象
type Role = "admin" | "user" | "guest";
interface Permissions {
canRead: boolean;
canWrite: boolean;
canDelete: boolean;
}
const rolePermissions: Record<Role, Permissions> = {
admin: { canRead: true, canWrite: true, canDelete: true },
user: { canRead: true, canWrite: true, canDelete: false },
guest: { canRead: true, canWrite: false, canDelete: false }
};
4.2 实现
ts
// Record 的实现原理
type MyRecord<K extends keyof any, T> = {
[P in K]: T;
};
// 源码
type Record<K extends keyof any, T> = {
[P in K]: T;
};
5. Pick
Pick<T, K> 从类型 T 中选择一组属性 K。
5.1 实例
ts
interface User {
id: number;
name: string;
email: string;
age: number;
isActive: boolean;
}
// 只选择用户的基本信息
type UserInfo = Pick<User, "name" | "email">;
const userInfo: UserInfo = {
name: "Alice",
email: "alice@example.com"
};
// 选择用户的身份信息
type UserIdentity = Pick<User, "id" | "name">;
function greetUser(user: UserIdentity): string {
return `Hello, ${user.name} (ID: ${user.id})`;
}
5.2 实现
ts
// Pick 的实现原理
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
// 源码
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
6. Omit
Omit<T, K> 从类型 T 中排除一组属性 K。
6.1 实例
ts
interface User {
id: number;
name: string;
email: string;
password: string;
age: number;
isActive: boolean;
}
// 排除敏感信息,创建安全的用户类型
type PublicUser = Omit<User, "password">;
function getUserProfile(userId: number): Promise<PublicUser> {
// 从数据库获取用户信息,但不包含密码
return fetch(`/api/users/${userId}`).then(res => res.json());
}
// 排除多个敏感属性
type SafeUser = Omit<User, "password" | "email">;
6.2 实现
ts
// Omit 的实现原理
type MyOmit<T, K extends keyof T> = {
[P in Exclude<keyof T, K>]: T[P];
}
// Omit 的实现原理
type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
// 源码
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
7. Exclude
Exclude<T, U> 从类型 T 中排除 U 类型的所有属性。
7.1 实例
ts
// 从联合类型中排除特定类型
type Status = "pending" | "approved" | "rejected" | "draft";
// 排除草稿状态
type PublishedStatus = Exclude<Status, "draft">;
// 排除多个状态
type ActiveStatus = Exclude<Status, "draft" | "rejected">;
// 在数字联合类型中使用
type Numbers = 1 | 2 | 3 | 4 | 5;
type OddNumbers = Exclude<Numbers, 2 | 4>;
7.2 实现
ts
// Exclude 的实现原理
type MyExclude<T, U> = T extends U ? never : T;
// 源码
type Exclude<T, U> = T extends U ? never : T;
8. Extract
Extract<T, U> 从类型 T 中提取 U 类型的所有属性。
8.1 实例
ts
// 从联合类型中提取特定类型
type Status = "pending" | "approved" | "rejected" | "draft";
// 提取已完成的状态
type CompletedStatus = Extract<Status, "approved" | "rejected">;
// 提取字符串类型
type Mixed = string | number | boolean;
type StringsOnly = Extract<Mixed, string>;
// 提取数字类型
type NumbersOnly = Extract<Mixed, number>;
8.2 实现
ts
// Extract 的实现原理
type MyExtract<T, U> = T extends U ? T : never;
// 源码
type Extract<T, U> = T extends U ? T : never;
9. NonNullable
NonNullable 从类型 T 中排除 null 和 undefined。
9.1 实例
ts
// 创建可能包含 null 或 undefined 的联合类型
type NullableString = string | null | undefined;
// 排除 null 和 undefined
type StrictString = NonNullable<NullableString>;
// 在函数参数中使用
function processText(text: NullableString): number {
// 使用类型断言或类型检查
const strictText: StrictString = text as StrictString;
return strictText.length; // 现在不会有 null 或 undefined 错误
}
// 在数组中使用
type NullableArray = (string | null)[];
type StrictArray = NonNullable<NullableArray>[number][];
9.2 实现
ts
// NonNullable 的实现原理
type MyNonNullable<T> = T extends null | undefined ? never : T;
// 源码
type NonNullable<T> = T & {};
10. Parameters
Parameters 从函数类型 T 中提取参数类型。
10.1 实例
ts
// 定义一个函数类型
function greet(name: string, age: number): string {
return `Hello, ${name}! You are ${age} years old.`;
}
// 获取函数的参数类型
type GreetParams = Parameters<typeof greet>;
// 使用参数类型
const params: GreetParams = ["Alice", 25];
// 在高阶函数中使用
function callWithParams<T extends (...args: any[]) => any>(
fn: T,
...args: Parameters<T>
): ReturnType<T> {
return fn(...args);
}
// 调用函数
const result = callWithParams(greet, "Bob", 30);
10.2 实现
ts
// Parameters 的实现原理
type MyParameters<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
// 源码
type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
11. ReturnType
ReturnType 从函数类型 T 中提取返回类型。
11.1 实例
ts
// 定义函数
function getUser(id: number) {
return {
id,
name: "Alice",
email: "alice@example.com",
isActive: true
};
}
// 获取函数的返回值类型
type UserType = ReturnType<typeof getUser>;
// 使用返回值类型
const user: UserType = {
id: 1,
name: "Bob",
email: "bob@example.com",
isActive: false
};
// 在异步函数中使用
async function fetchUser(id: number) {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
type FetchUserReturnType = ReturnType<typeof fetchUser>;
type UserDataType = Awaited<FetchUserReturnType>; // 获取 Promise 解析后的类型
11.2 实现
ts
// ReturnType 的实现原理
type MyReturnType<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : any;
// 源码
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
12. 总结
TypeScript 的泛型工具类型为我们提供了强大的类型操作能力:
- Partial 和 Required 允许我们控制属性的可选性;
- Readonly 帮助我们创建不可变的数据结构;
- Record<K, T> 用于创建键值对映射类型;
- Pick<T, K> 和 Omit<T, K> 允许我们选择或排除特定属性;
- Exclude<T, U> 和 Extract<T, U> 用于从联合类型中提取或排除类型;
- NonNullable 从类型中排除 null 和 undefined;
- Parameters 和 ReturnType 用于提取函数参数和返回值类型。