泛型(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++模板编程;现代编程语言类型系统研究。
