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);
}

最佳实践总结

  1. 合理使用泛型:只在需要类型重用时使用泛型
  2. 提供有意义的约束:使用extends关键字限制泛型范围
  3. 优先使用工具类型:充分利用TypeScript内置的工具类型
  4. 保持简单:避免过度复杂的泛型定义
  5. 文档化复杂类型:为复杂的泛型类型添加注释说明
  6. 性能考虑:避免过于复杂的递归类型定义

TypeScript的高级泛型特性为我们提供了强大的类型系统工具,合理使用这些特性可以显著提升代码的类型安全性和可维护性。通过掌握这些技巧,你将能够编写出更加优雅、类型安全的TypeScript代码。