Skip to content
On this page

TypeScript 泛型

泛型是 TypeScript 中一个强大的特性,它允许我们创建可重用的组件,这些组件可以处理多种类型的数据,同时保持类型安全。

泛型基础

泛型函数

typescript
// 不使用泛型的函数
function identity1(arg: number): number {
  return arg;
}

// 使用 any 类型的函数(丢失了类型信息)
function identity2(arg: any): any {
  return arg;
}

// 使用泛型的函数
function identity<T>(arg: T): T {
  return arg;
}

// 调用泛型函数 - 明确指定类型
let output1 = identity<string>("myString");

// 调用泛型函数 - 类型推断
let output2 = identity("myString"); // TypeScript 自动推断 T 为 string

泛型类型变量

typescript
function loggingIdentity<T>(arg: T): T {
  // console.log(arg.length);  // 错误:T 不一定有 .length 属性
  return arg;
}

// 使用数组类型的泛型函数
function loggingIdentity2<T>(arg: T[]): T[] {
  console.log(arg.length);  // 正确:T[] 有 .length 属性
  return arg;
}

// 等价于上面的写法
function loggingIdentity3<T>(arg: Array<T>): Array<T> {
  console.log(arg.length);
  return arg;
}

泛型类型

泛型接口

typescript
interface GenericIdentityFn<T> {
  (arg: T): T;
}

function identity<T>(arg: T): T {
  return arg;
}

let myIdentity: GenericIdentityFn<number> = identity;

泛型类

typescript
class GenericNumber<T> {
  zeroValue: T;
  add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

// 泛型类的字符串版本
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };

泛型约束

基本约束

typescript
interface Lengthwise {
  length: number;
}

function loggingIdentity4<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);  // 现在可以安全地访问 .length 属性
  return arg;
}

loggingIdentity4({ length: 10, value: 3 });  // 正确
// loggingIdentity4(3);  // 错误:number 类型没有 .length 属性

在泛型约束中使用类型参数

typescript
function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // 正确
// getProperty(x, "m"); // 错误:'m' 不是 'a' | 'b' | 'c' | 'd' 中的任何一种

在泛型里使用类类型

typescript
function create<T>(c: { new(): T }): T {
  return new c();
}

// 使用更复杂的工厂函数
class BeeKeeper {
  hasMask: boolean = true;
}

class ZooKeeper {
  nametag: string = "Mikle";
}

class Animal {
  numLegs: number = 4;
}

class Bee extends Animal {
  keeper: BeeKeeper = new BeeKeeper();
}

class Lion extends Animal {
  keeper: ZooKeeper = new ZooKeeper();
}

function createInstance<A extends Animal>(c: new () => A): A {
  return new c();
}

createInstance(Lion).keeper.nametag;  // 类型为 string
createInstance(Bee).keeper.hasMask;   // 类型为 boolean

高级泛型模式

泛型工具类型

TypeScript 提供了一些内置的泛型工具类型:

typescript
// Partial<T> - 将所有属性变为可选
interface Todo {
  title: string;
  description: string;
}

function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
  return { ...todo, ...fieldsToUpdate };
}

// Pick<T, K> - 从 T 中选择部分属性
type TodoPreview = Pick<Todo, "title">;
const todo3: TodoPreview = {
  title: "Clean room"
};

// Record<K, T> - 创建一个对象类型,键为 K,值为 T
interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const x: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" }
};

// Omit<T, K> - 从 T 中省略部分属性
interface Todo2 {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview2 = Omit<Todo2, "description">;

const todo4: TodoPreview2 = {
  title: "Clean room",
  completed: false,
};

// Exclude<T, U> - 从 T 中排除 U
type T0 = Exclude<"a" | "b" | "c", "a">;  // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;  // "c"
type T2 = Exclude<string | number | (() => void), Function>;  // string | number

// Extract<T, U> - 从 T 中提取 U
type T3 = Extract<"a" | "b" | "c", "a" | "f">;  // "a"
type T4 = Extract<string | number | (() => void), Function>;  // () => void

// NonNullable<T> - 从 T 中排除 null 和 undefined
type T5 = NonNullable<string | number | undefined>; // string | number
type T6 = NonNullable<string[] | null | undefined>; // string[]

// ReturnType<T> - 获取函数返回值类型
type T7 = ReturnType<() => string>;  // string
type T8 = ReturnType<(s: string) => void>;  // void
type T9 = ReturnType<<T>() => T>;  // {}
type T10 = ReturnType<<T extends U, U extends number[]>() => T>;  // number[]

// InstanceType<T> - 获取构造函数实例类型
class C {
  x = 0;
  y = 0;
}

type T11 = InstanceType<typeof C>;  // C
type T12 = InstanceType<any>;  // any
type T13 = InstanceType<never>;  // never
// type T14 = InstanceType<string>;  // 错误

条件类型

typescript
// 条件类型的基本形式:T extends U ? X : Y
type TypeName<T> = T extends string ? "string" : 
                  T extends number ? "number" : 
                  T extends boolean ? "boolean" : 
                  "other";

type T0 = TypeName<string>;  // "string"
type T1 = TypeName<"a">;     // "string"
type T2 = TypeName<true>;    // "boolean"

// 分配条件类型
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;

type T20 = Boxed<string>;        // BoxedValue<string>
type T21 = Boxed<number[]>;      // BoxedArray<number>
type T22 = Boxed<string | number[]>; // BoxedValue<string> | BoxedArray<number>

泛型条件类型

typescript
// infer 关键字用于推断类型
type Flatten<T> = T extends Array<infer Item> ? Item : T;

type T1 = Flatten<number[]>;      // number
type T2 = Flatten<string>;        // string
type T3 = Flatten<readonly string[]>; // string

// 更复杂的 infer 使用
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type T4 = GetReturnType<() => number>;  // number
type T5 = GetReturnType<(x: string) => boolean>;  // boolean

泛型与类、接口的高级应用

泛型约束与默认类型

typescript
// 泛型参数的默认类型
interface A<T = string> {
  name: T;
}

const a1: A<number> = { name: 123 };  // T 是 number
const a2: A = { name: "hello" };      // T 是默认的 string

多泛型参数

typescript
// 使用多个泛型参数
function processTuple<T, U>(tuple: [T, U]): [U, T] {
  return [tuple[1], tuple[0]];
}

const result = processTuple(["hello", 42]); // [number, string]

泛型工厂模式

typescript
interface IFactory<T> {
  create(): T;
}

class Foo {
  constructor(public name: string) {}
}

class Bar {
  constructor(public age: number) {}
}

const fooFactory: IFactory<Foo> = {
  create: () => new Foo("default")
};

const barFactory: IFactory<Bar> = {
  create: () => new Bar(25)
};

泛型最佳实践

类型安全的 CRUD 操作

typescript
interface Entity {
  id: number;
}

interface User extends Entity {
  name: string;
  email: string;
}

interface Product extends Entity {
  title: string;
  price: number;
}

class Repository<T extends Entity> {
  private entities: T[] = [];

  add(entity: T): T {
    this.entities.push(entity);
    return entity;
  }

  findById(id: number): T | undefined {
    return this.entities.find(e => e.id === id);
  }

  getAll(): T[] {
    return [...this.entities];
  }

  update(id: number, update: Partial<T>): T | undefined {
    const index = this.entities.findIndex(e => e.id === id);
    if (index !== -1) {
      this.entities[index] = { ...this.entities[index], ...update };
      return this.entities[index];
    }
    return undefined;
  }

  delete(id: number): boolean {
    const initialLength = this.entities.length;
    this.entities = this.entities.filter(e => e.id !== id);
    return initialLength > this.entities.length;
  }
}

// 使用示例
const userRepository = new Repository<User>();
const user = userRepository.add({ id: 1, name: "John", email: "john@example.com" });

const productRepository = new Repository<Product>();
const product = productRepository.add({ id: 1, title: "Laptop", price: 999 });

小结

泛型是 TypeScript 中非常强大的特性,它允许我们编写可重用且类型安全的代码。通过泛型,我们可以创建适用于多种类型的组件,同时保留类型信息,提高代码的灵活性和可维护性。掌握泛型的概念和用法对于编写高质量的 TypeScript 代码至关重要。