TypeScript高级泛型技巧:构建类型安全且灵活的代码
TypeScript高级泛型技巧详解,包含条件类型、映射类型、模板字面量类型等高级特性,提升代码类型安全性和灵活性
TypeScript的泛型系统是其最强大的特性之一,它让我们能够编写可重用、类型安全的代码。本文将深入探讨TypeScript泛型的高级技巧,帮助你充分发挥这个强大工具的潜力。
泛型基础回顾#
基本泛型语法#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| // 基本泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 泛型接口
interface Box<T> {
value: T;
}
// 泛型类
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
}
|
条件类型 (Conditional Types)#
条件类型允许我们根据类型关系来选择类型,这是TypeScript 2.8引入的强大特性。
基本条件类型#
1
2
3
4
5
6
7
8
9
10
11
| // 基本语法
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<string>; // true
type Test2 = IsString<number>; // false
// 实用示例:提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type StringArray = string[];
type ElementType = ArrayElement<StringArray>; // string
|
条件类型与泛型约束结合#
1
2
3
4
5
6
7
8
9
10
11
12
| // 泛型约束 + 条件类型
interface HasLength {
length: number;
}
type GetLength<T extends HasLength> = T['length'];
// 复杂的条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type Example1 = NonNullable<string>; // string
type Example2 = NonNullable<null>; // never
|
映射类型 (Mapped Types)#
映射类型允许我们基于旧类型创建新类型,通过遍历属性来转换类型。
基本映射类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| // 将所有属性设为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial<User>;
// 结果: { id?: number; name?: string; email?: string; }
// 将所有属性设为必需
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 只读映射
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
|
高级映射类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 选择特定属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type UserWithoutEmail = Pick<User, 'id' | 'name'>;
// 结果: { id: number; name: string; }
// 排除特定属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type UserWithoutId = Omit<User, 'id'>;
// 结果: { name: string; email: string; }
// 转换属性类型
type Stringify<T> = {
[K in keyof T]: string;
};
type StringifiedUser = Stringify<User>;
// 结果: { id: string; name: string; email: string; }
|
模板字面量类型 (Template Literal Types)#
TypeScript 4.1引入了模板字面量类型,让我们可以操作字符串类型。
基本模板字面量#
1
2
3
4
5
6
7
8
9
10
11
12
| type Greeting = `Hello, ${string}!`;
const message: Greeting = "Hello, World!"; // ✅
const invalid: Greeting = "Hi, World!"; // ❌
// 联合类型与模板字面量
type Color = "red" | "green" | "blue";
type Shade = "light" | "dark";
type ColorShade = `${Shade}-${Color}`;
// 结果: "light-red" | "light-green" | "light-blue" |
// "dark-red" | "dark-green" | "dark-blue"
|
实际应用示例#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| // CSS属性生成器
type CSSProperty = `padding${"Top" | "Right" | "Bottom" | "Left"}` |
`margin${"Top" | "Right" | "Bottom" | "Left"}` |
`border${"Top" | "Right" | "Bottom" | "Left"}${"Width" | "Color" | "Style"}`;
// 函数名生成器
type Getter<T> = `get${Capitalize<string & T>}`;
type Setter<T> = `set${Capitalize<string & T>}`;
interface UserInfo {
name: string;
age: number;
}
type UserGetter = Getter<keyof UserInfo>;
// 结果: "getName" | "getAge"
type UserSetter = Setter<keyof UserInfo>;
// 结果: "setName" | "setAge"
|
高级泛型模式#
递归类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
interface Nested {
user: {
name: string;
address: {
city: string;
};
};
}
type DeepReadonlyNested = DeepReadonly<Nested>;
// 所有嵌套属性都变为只读
// 深度Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
|
函数重载与泛型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
| // 泛型重载
function createElement<T extends string>(
tag: T,
props?: Record<string, any>,
children?: any[]
): T;
function createElement<T>(
tag: T,
props?: Record<string, any>,
children?: any[]
): T;
// 类型安全的Redux Action Creator
type Action<T extends string, P = {}> = {
type: T;
payload?: P;
};
interface ActionCreators<T> {
[K in keyof T]: T[K] extends (...args: any[]) => infer A ? A : never;
}
function createAction<T extends string>(type: T): () => Action<T>;
function createAction<T extends string, P>(type: T, payloadCreator: (payload: P) => P): (payload: P) => Action<T, P>;
|
高级工具类型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| // 提取Promise的解析类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type AsyncFunction = () => Promise<string>;
type Result = UnwrapPromise<AsyncFunction>; // string
// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type FunctionType = (x: number, y: string) => boolean;
type Params = Parameters<FunctionType>; // [number, string]
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type FuncReturn = ReturnType<FunctionType>; // boolean
// 构造函数参数提取
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
// 实例类型提取
type InstanceType<T> = T extends new (...args: any[]) => infer R ? R : any;
class MyClass {
constructor(public id: number, public name: string) {}
}
type MyClassParams = ConstructorParameters<typeof MyClass>; // [number, string]
type MyClassInstance = InstanceType<typeof MyClass>; // MyClass
|
实际应用场景#
1. 类型安全的API客户端#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
interface User {
id: number;
name: string;
email: string;
}
class ApiClient {
async get<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
return response.json();
}
async getUser(id: number): Promise<ApiResponse<User>> {
return this.get<User>(`/api/users/${id}`);
}
}
|
2. 状态管理中的泛型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
| interface State<T> {
data: T | null;
loading: boolean;
error: string | null;
}
interface Action<T, P> {
type: string;
payload?: P;
}
type Reducer<S, A extends Action<string, any>> = (
state: S,
action: A
) => S;
// 类型安全的Redux reducer
function createReducer<S, A extends Action<string, any>>(
initialState: S,
handlers: Record<string, (state: S, action: A) => S>
): Reducer<S, A> {
return (state = initialState, action) => {
const handler = handlers[action.type];
return handler ? handler(state, action) : state;
};
}
|
3. 表单验证器#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
| interface ValidationRule<T> {
validate: (value: T) => boolean;
message: string;
}
type ValidationSchema<T> = {
[K in keyof T]?: ValidationRule<T[K]>[];
};
class FormValidator<T extends Record<string, any>> {
constructor(private schema: ValidationSchema<T>) {}
validate(data: T): { isValid: boolean; errors: Partial<Record<keyof T, string[]>> } {
const errors: Partial<Record<keyof T, string[]>> = {};
for (const key in this.schema) {
const rules = this.schema[key];
if (rules && data[key] !== undefined) {
const fieldErrors = rules
.filter(rule => !rule.validate(data[key]))
.map(rule => rule.message);
if (fieldErrors.length > 0) {
errors[key] = fieldErrors;
}
}
}
return {
isValid: Object.keys(errors).length === 0,
errors
};
}
}
// 使用示例
interface LoginForm {
email: string;
password: string;
}
const loginValidator = new FormValidator<LoginForm>({
email: [
{ validate: (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email), message: "邮箱格式无效" }
],
password: [
{ validate: (password) => password.length >= 8, message: "密码至少8位" }
]
});
|
性能优化建议#
1. 避免过度复杂的泛型#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| // ❌ 过于复杂,影响编译性能
type UltraComplex<T> = T extends { [K in keyof T]: infer U }
? U extends { [P in keyof U]: infer V }
? V extends (...args: any[]) => infer R
? R extends Promise<infer PR>
? PR extends Array<infer A>
? A extends { [Q in keyof A]: infer B }
? B extends string ? B : never
: never
: never
: never
: never
: never;
// ✅ 简化类型定义
type SimpleType<T> = T extends (...args: any[]) => infer R ? R : never;
|
2. 使用类型别名减少重复#
1
2
3
4
5
6
7
8
| // ❌ 重复的类型定义
function process1<T extends { id: number }>(item: T): T['id'] { return item.id; }
function process2<T extends { id: number }>(item: T): T['id'] { return item.id; }
// ✅ 提取公共类型
type WithId<T> = T & { id: number };
function process1<T>(item: WithId<T>): number { return item.id; }
function process2<T>(item: WithId<T>): number { return item.id; }
|
3. 合理使用泛型约束#
1
2
3
4
5
6
7
8
9
10
11
| // ✅ 明确的约束提高类型安全性
interface HasId {
id: number;
}
function getById<T extends HasId>(
items: T[],
id: number
): T | undefined {
return items.find(item => item.id === id);
}
|
最佳实践总结#
- 合理使用泛型:只在需要类型重用时使用泛型
- 提供有意义的约束:使用extends关键字限制泛型范围
- 优先使用工具类型:充分利用TypeScript内置的工具类型
- 保持简单:避免过度复杂的泛型定义
- 文档化复杂类型:为复杂的泛型类型添加注释说明
- 性能考虑:避免过于复杂的递归类型定义
TypeScript的高级泛型特性为我们提供了强大的类型系统工具,合理使用这些特性可以显著提升代码的类型安全性和可维护性。通过掌握这些技巧,你将能够编写出更加优雅、类型安全的TypeScript代码。