泛型(Generics)是现代编程语言中强大的类型系统特性,它允许我们编写可复用的代码组件,同时保持类型安全。从C++的模板到Java的泛型,再到TypeScript的泛型系统,泛型编程已经成为现代软件开发的重要技能。本文将深入讲解泛型的核心概念、设计模式、高级技巧以及在TypeScript中的实际应用。

一、泛型的核心概念

泛型的本质是参数化类型,它允许我们在定义函数、接口、类时使用类型参数,从而创建适用于多种类型的通用代码。

1.1 类型参数与类型变量

// 基础泛型函数
function identity(arg: T): T {
  return arg;
}

// 使用泛型
const num = identity(123); // 类型为 number
const str = identity('hello'); // 类型推断为 string

const bool = identity(true); // 类型为 boolean

1.2 泛型接口与类型约束

// 泛型接口
interface Box {
  value: T;
  getValue(): T;
  setValue(value: T): void;
}

// 类型约束
interface Lengthwise {
  length: number;
}

function getLength(arg: T): number {
  return arg.length;
}

getLength('hello'); // OK
getLength([1, 2, 3]); // OK
// getLength(123); // Error: number 没有 length 属性

二、泛型函数与类型推断

2.1 多类型参数

// 多个类型参数
function pair(first: T, second: U): [T, U] {
  return [first, second];
}

const result = pair('hello', 123);
// 类型为 [string, number]

2.2 类型推断与默认类型参数

// 类型推断
function createArray(length: number, value: T): T[] {
  return Array(length).fill(value);
}

const strArray = createArray(3, 'hello'); // 类型推断为 string[]
const numArray = createArray(3, 123); // 类型推断为 number[]

// 默认类型参数
function createElement(tag: string): T {
  return document.createElement(tag) as T;
}

三、泛型类与接口

3.1 泛型类

// 泛型类
class Storage {
  private items: T[] = [];
  
  add(item: T): void {
    this.items.push(item);
  }
  
  get(index: number): T | undefined {
    return this.items[index];
  }
  
  getAll(): T[] {
    return [...this.items];
  }
}

// 使用泛型类
const stringStorage = new Storage();
stringStorage.add('hello');
stringStorage.add('world');
console.log(stringStorage.getAll()); // ['hello', 'world']

const numberStorage = new Storage();
numberStorage.add(1);
numberStorage.add(2);
console.log(numberStorage.getAll()); // [1, 2]

3.2 泛型接口与类型别名

// 泛型接口
interface Response {
  data: T;
  status: number;
  message: string;
}

// 泛型类型别名
type Nullable = T | null;
type Optional = T | undefined;
type Pair = [T, U];

// 使用示例
const userResponse: Response = {
  data: { id: 1, name: '张三' },
  status: 200,
  message: 'Success'
};

const nullableName: Nullable = '张三' || null;
const optionalAge: Optional = undefined;

四、高级泛型技巧

4.1 条件类型

// 条件类型
type NonNullable = T extends null | undefined ? never : T;
type IsArray = T extends any[] ? true : false;

// 使用示例
type NonNullableString = NonNullable; // string
type IsStringArray = IsArray; // true
type IsNumberArray = IsArray; // true

4.2 映射类型

// 映射类型
type Readonly = {
  readonly [P in keyof T]: T[P];
};

type Partial = {
  [P in keyof T]?: T[P];
};

type Required = {
  [P in keyof T]-?: T[P];
};

// 使用示例
interface User {
  id: number;
  name: string;
  email: string;
}

type ReadonlyUser = Readonly;
type PartialUser = Partial;

const readonlyUser: ReadonlyUser = {
  id: 1,
  name: '张三',
  email: 'zhangsan@example.com'
};
// readonlyUser.id = 2; // Error: Cannot assign to 'id' because it is read-only

const partialUser: PartialUser = {
  name: '李四' // OK,其他属性可选
};

4.3 工具类型

// 内置工具类型
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
}

// Pick:选择部分属性
type UserBasic = Pick;
// { id: number; name: string; }

// Omit:排除部分属性
type UserPublic = Omit;
// { id: number; name: string; age?: number; }

// Record:创建对象类型
type StringMap = Record;
// { [key: string]: number; }

// Extract:提取联合类型中的部分
type StringOrNumber = string | number;
type ExtractedString = Extract; // string

五、泛型设计模式

5.1 工厂模式

// 泛型工厂模式
interface Creator {
  create(): T;
}

class UserCreator implements Creator {
  create(): User {
    return { id: 1, name: '张三' };
  }
}

class ProductCreator implements Creator {
  create(): Product {
    return { id: 1, name: '商品1', price: 100 };
  }
}

// 使用工厂
function createEntity(creator: Creator): T {
  return creator.create();
}

const user = createEntity(new UserCreator());
const product = createEntity(new ProductCreator());

5.2 策略模式

// 泛型策略模式
interface SortStrategy {
  compare(a: T, b: T): number;
}

class NumberSortStrategy implements SortStrategy {
  compare(a: number, b: number): number {
    return a - b;
  }
}

class StringSortStrategy implements SortStrategy {
  compare(a: string, b: string): number {
    return a.localeCompare(b);
  }
}

// 使用策略模式
function sortArray(arr: T[], strategy: SortStrategy): T[] {
  return [...arr].sort((a, b) => strategy.compare(a, b));
}

const numbers = [3, 1, 4, 1, 5];
const strings = ['banana', 'apple', 'cherry'];

const sortedNumbers = sortArray(numbers, new NumberSortStrategy());
const sortedStrings = sortArray(strings, new StringSortStrategy());

5.3 仓储模式

// 泛型仓储模式
interface Repository {
  findById(id: number): T | undefined;
  findAll(): T[];
  save(entity: T): void;
  delete(id: number): void;
}

class UserRepository implements Repository {
  private users: User[] = [];
  
  findById(id: number): User | undefined {
    return this.users.find(user => user.id === id);
  }
  
  findAll(): User[] {
    return [...this.users];
  }
  
  save(user: User): void {
    this.users.push(user);
  }
  
  delete(id: number): void {
    this.users = this.users.filter(user => user.id !== id);
  }
}

// 使用仓储
function manageEntity(repository: Repository, id: number) {
  const entity = repository.findById(id);
  if (entity) {
    console.log('Found:', entity);
  } else {
    console.log('Not found');
  }
}

const userRepo = new UserRepository();
manageEntity(userRepo, 1);

六、泛型在实战中的应用

6.1 API响应类型

// API响应泛型类型
interface ApiResponse {
  success: boolean;
  data: T;
  message: string;
  code: number;
}

interface User {
  id: number;
  name: string;
  email: string;
}

interface Post {
  id: number;
  title: string;
  content: string;
}

// 使用泛型API响应
async function fetchUser(): Promise> {
  const response = await fetch('/api/user');
  return response.json();
}

async function fetchPost(): Promise> {
  const response = await fetch('/api/post');
  return response.json();
}

// 使用示例
const userResponse = await fetchUser();
if (userResponse.success) {
  console.log(userResponse.data.name); // TypeScript 知道这是 User 类型
}

const postResponse = await fetchPost();
if (postResponse.success) {
  console.log(postResponse.data.title); // TypeScript 知道这是 Post 类型
}

6.2 组件状态管理

// 泛型状态管理
class Store {
  private state: T;
  private listeners: ((state: T) => void)[] = [];
  
  constructor(initialState: T) {
    this.state = initialState;
  }
  
  getState(): T {
    return this.state;
  }
  
  setState(newState: Partial): void {
    this.state = { ...this.state, ...newState };
    this.listeners.forEach(listener => listener(this.state));
  }
  
  subscribe(listener: (state: T) => void): () => void {
    this.listeners.push(listener);
    return () => {
      this.listeners = this.listeners.filter(l => l !== listener);
    };
  }
}

// 定义状态接口
interface AppState {
  user: User | null;
  isLoading: boolean;
  error: string | null;
}

// 使用泛型状态管理
const appStore = new Store({
  user: null,
  isLoading: false,
  error: null
});

appStore.subscribe((state) => {
  console.log('State changed:', state);
});

appStore.setState({ isLoading: true });

6.3 数据结构实现

// 泛型栈实现
class Stack {
  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;
  }
  
  size(): number {
    return this.items.length;
  }
}

// 泛型队列实现
class Queue {
  private items: T[] = [];
  
  enqueue(item: T): void {
    this.items.push(item);
  }
  
  dequeue(): T | undefined {
    return this.items.shift();
  }
  
  front(): T | undefined {
    return this.items[0];
  }
  
  isEmpty(): boolean {
    return this.items.length === 0;
  }
}

// 使用示例
const numberStack = new Stack();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2

const stringQueue = new Queue();
stringQueue.enqueue('first');
stringQueue.enqueue('second');
console.log(stringQueue.dequeue()); // 'first'

七、泛型最佳实践与常见陷阱

7.1 最佳实践

  • 保持类型安全:避免使用any,合理使用泛型约束
  • 合理命名:使用T、U、V等有意义的类型参数名称
  • 复用工具类型:善用Pick、Omit、Partial等内置工具类型
  • 文档完善:为泛型类型添加清晰的注释和文档
  • 测试覆盖:确保泛型代码在各种类型下都能正常工作

7.2 常见陷阱

  • 过度泛化:泛型应该提高代码复用性,而不是过度设计
  • 类型断言滥用:避免过度使用类型断言,优先使用类型守卫
  • 约束缺失:泛型参数应该有适当的约束,否则可能失去类型安全
  • 循环依赖:复杂的泛型类型可能导致循环依赖,影响编译性能
  • 类型推断失效:某些情况下类型推断可能失效,需要显式指定类型

八、总结:泛型的艺术

泛型是现代类型系统的重要组成部分,它通过参数化类型的方式,让我们能够编写既通用又类型安全的代码。从基础的类型参数到高级的条件类型,从设计模式到实战应用,泛型为软件开发提供了强大的抽象能力。掌握泛型编程,不仅能提高代码质量和开发效率,更能体现开发者对类型系统的深刻理解。

“泛型不是复杂的语法糖,而是编程思维的体现。它教会我们如何抽象、如何复用、如何保持类型安全。” —— 本文作者注

参考文献:TypeScript官方文档 — Generics;Java泛型指南;C++模板编程;现代编程语言类型系统研究。