泛型(Generics)

泛型是 TypeScript 最强大的特性之一,允许编写可重用、类型安全的代码。


泛型基础

为什么需要泛型

// 问题:使用 any 会丢失类型信息
function identity(value: any): any {
  return value;
}
 
const result = identity("hello"); // any,丢失了 string 类型
 
// 解决:使用泛型保留类型信息
function identityGeneric<T>(value: T): T {
  return value;
}
 
const result2 = identityGeneric("hello"); // string,类型被保留

基本语法

// 泛型函数
function first<T>(arr: T[]): T | undefined {
  return arr[0];
}
 
// 显式指定类型
first<number>([1, 2, 3]);     // number | undefined
 
// 类型推断
first([1, 2, 3]);             // number | undefined
first(["a", "b"]);            // string | undefined

多个类型参数

function swap<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}
 
const swapped = swap([1, "hello"]); // [string, number]
 
// 常见命名约定
// T - Type(类型)
// K - Key(键)
// V - Value(值)
// E - Element(元素)
// R - Return(返回值)

泛型约束

extends 约束

interface Lengthwise {
  length: number;
}
 
function logLength<T extends Lengthwise>(value: T): T {
  console.log(value.length);
  return value;
}
 
logLength("hello");      // ✅ string 有 length
logLength([1, 2, 3]);    // ✅ 数组有 length
logLength({ length: 10 }); // ✅ 有 length 属性
// logLength(123);       // ❌ number 没有 length

keyof 约束

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
 
const user = { name: "Alice", age: 25 };
 
getProperty(user, "name"); // string
getProperty(user, "age");  // number
// getProperty(user, "email"); // ❌ "email" 不是 user 的键

多重约束

interface Printable {
  print(): void;
}
 
interface Loggable {
  log(): void;
}
 
function process<T extends Printable & Loggable>(value: T): void {
  value.print();
  value.log();
}

泛型接口

// 泛型接口
interface Container<T> {
  value: T;
  getValue(): T;
  setValue(value: T): void;
}
 
// 实现泛型接口
class Box<T> implements Container<T> {
  constructor(public value: T) {}
  
  getValue(): T {
    return this.value;
  }
  
  setValue(value: T): void {
    this.value = value;
  }
}
 
const numberBox = new Box<number>(42);
const stringBox = new Box<string>("hello");

泛型接口作为函数类型

interface Comparator<T> {
  (a: T, b: T): number;
}
 
const numberComparator: Comparator<number> = (a, b) => a - b;
const stringComparator: Comparator<string> = (a, b) => a.localeCompare(b);

泛型类

class Stack<T> {
  private items: T[] = [];
 
  push(item: T): void {
    this.items.push(item);
  }
 
  pop(): T | undefined {
    return this.items.pop();
  }
 
  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
 
  isEmpty(): boolean {
    return this.items.length === 0;
  }
}
 
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.pop(); // 2
 
const stringStack = new Stack<string>();
stringStack.push("hello");

泛型类的静态成员

class GenericClass<T> {
  // 静态成员不能使用类的泛型参数
  // static value: T; // ❌ 错误
 
  // 静态成员可以有自己的泛型
  static create<U>(value: U): GenericClass<U> {
    const instance = new GenericClass<U>();
    instance.value = value;
    return instance;
  }
 
  value!: T;
}

泛型类型别名

// 简单泛型类型
type Nullable<T> = T | null;
type Optional<T> = T | undefined;
 
let name: Nullable<string> = null;
name = "Alice";
 
// 复杂泛型类型
type Result<T, E = Error> = 
  | { success: true; data: T }
  | { success: false; error: E };
 
function divide(a: number, b: number): Result<number, string> {
  if (b === 0) {
    return { success: false, error: "Division by zero" };
  }
  return { success: true, data: a / b };
}

泛型默认类型

interface Response<T = unknown> {
  data: T;
  status: number;
}
 
// 使用默认类型
const response1: Response = { data: "hello", status: 200 };
 
// 指定具体类型
const response2: Response<{ name: string }> = {
  data: { name: "Alice" },
  status: 200
};

带约束的默认类型

interface Container<T extends object = { id: number }> {
  data: T;
}
 
const c1: Container = { data: { id: 1 } }; // 使用默认类型
const c2: Container<{ name: string }> = { data: { name: "test" } };

内置工具类型

TypeScript 提供了许多基于泛型的工具类型:

Partial

将所有属性变为可选:

interface User {
  name: string;
  age: number;
  email: string;
}
 
type PartialUser = Partial<User>;
// { name?: string; age?: number; email?: string }
 
function updateUser(user: User, updates: Partial<User>): User {
  return { ...user, ...updates };
}

Required

将所有属性变为必选:

interface Config {
  url?: string;
  timeout?: number;
}
 
type RequiredConfig = Required<Config>;
// { url: string; timeout: number }

Readonly

将所有属性变为只读:

type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; readonly email: string }

Pick<T, K>

选取指定属性:

type UserBasic = Pick<User, "name" | "email">;
// { name: string; email: string }

Omit<T, K>

排除指定属性:

type UserWithoutEmail = Omit<User, "email">;
// { name: string; age: number }

Record<K, V>

创建键值对类型:

type UserRoles = Record<string, string[]>;
// { [key: string]: string[] }
 
const roles: UserRoles = {
  admin: ["read", "write", "delete"],
  user: ["read"]
};

Extract<T, U> 和 Exclude<T, U>

type T = "a" | "b" | "c";
 
type Extracted = Extract<T, "a" | "b">;  // "a" | "b"
type Excluded = Exclude<T, "a">;         // "b" | "c"

NonNullable

type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>; // string

ReturnType 和 Parameters

function greet(name: string, age: number): string {
  return `${name} is ${age}`;
}
 
type GreetReturn = ReturnType<typeof greet>;     // string
type GreetParams = Parameters<typeof greet>;     // [string, number]

高级泛型模式

条件泛型

type IsArray<T> = T extends any[] ? true : false;
 
type A = IsArray<number[]>;  // true
type B = IsArray<string>;    // false
 
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
 
type C = ElementType<number[]>;  // number
type D = ElementType<string[]>;  // string

分布式条件类型

type ToArray<T> = T extends any ? T[] : never;
 
// 联合类型会被分布处理
type Result = ToArray<string | number>;
// string[] | number[](不是 (string | number)[])

映射泛型

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
 
interface Person {
  name: string;
  age: number;
}
 
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }

最佳实践

  1. 使用描述性类型参数名:复杂场景使用 TDataTResult
  2. 适当使用约束:限制泛型范围,提供更好的类型提示
  3. 提供默认类型:让 API 更易用
  4. 避免过度泛型化:简单场景不需要泛型
  5. 组合工具类型:复用 TypeScript 内置类型
// 推荐:组合使用工具类型
type UpdatePayload<T> = Partial<Omit<T, "id" | "createdAt">>;
 
interface User {
  id: number;
  name: string;
  email: string;
  createdAt: Date;
}
 
type UserUpdate = UpdatePayload<User>;
// { name?: string; email?: string }

typescript 泛型 类型系统