TypeScript 泛型(Generics)完全指南
目录
概述
什么是泛型?
泛型是一种在定义函数、接口或类时不预先指定具体类型,而是在使用时再指定类型的特性。它可以提高代码的复用性和类型安全性。
为什么使用泛型?
- 代码复用
- 类型安全
- 减少冗余代码
- 提高灵活性
基本用法
1. 泛型函数
// 基本泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用方式
let output1 = identity<string>("hello"); // 显式指定类型
let output2 = identity("hello"); // 类型推断
// 泛型箭头函数
const identityArrow = <T>(arg: T): T => {
return arg;
};
2. 多个类型参数
// 使用多个泛型类型
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// 使用
const result = pair<string, number>("hello", 42);
const result2 = pair("hello", 42); // 类型推断
3. 泛型数组
// 泛型数组
function reverseArray<T>(array: T[]): T[] {
return array.reverse();
}
// 使用
const numbers = reverseArray([1, 2, 3]);
const strings = reverseArray<string>(["a", "b", "c"]);
泛型约束
1. extends 关键字
// 使用接口定义约束
interface Lengthwise {
length: number;
}
// 约束泛型必须包含 length 属性
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在可以安全访问 length 属性
return arg;
}
// 使用
loggingIdentity("hello"); // 可以,字符串有 length
loggingIdentity([1, 2, 3]); // 可以,数组有 length
loggingIdentity({ length: 10, value: 3 }); // 可以,对象包含 length
2. 多重约束
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
// 多重约束
function printNameAndAge<T extends HasName & HasAge>(obj: T): void {
console.log(`${obj.name} is ${obj.age} years old`);
}
// 使用
printNameAndAge({ name: "John", age: 30, occupation: "developer" });
泛型类
1. 基本泛型类
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
getValue(): T {
return this.value;
}
setValue(value: T): void {
this.value = value;
}
}
// 使用
const numberContainer = new Container<number>(123);
const stringContainer = new Container<string>("hello");
2. 泛型类与继承
class DataStorage<T extends string | number | boolean> {
private data: T[] = [];
addItem(item: T) {
this.data.push(item);
}
removeItem(item: T) {
const index = this.data.indexOf(item);
if (index !== -1) {
this.data.splice(index, 1);
}
}
getItems(): T[] {
return [...this.data];
}
}
// 使用
const textStorage = new DataStorage<string>();
const numberStorage = new DataStorage<number>();
泛型接口
1. 基本泛型接口
interface Response<T> {
data: T;
status: number;
message: string;
}
// 使用
interface User {
id: number;
name: string;
}
function fetchUser(): Promise<Response<User>> {
// 实现...
return Promise.resolve({
data: { id: 1, name: "John" },
status: 200,
message: "Success"
});
}
2. 泛型方法接口
interface Repository<T> {
find(id: string): Promise<T>;
save(item: T): Promise<T>;
update(id: string, item: Partial<T>): Promise<T>;
delete(id: string): Promise<void>;
}
// 实现
class UserRepository implements Repository<User> {
async find(id: string): Promise<User> {
// 实现...
return null as any;
}
async save(user: User): Promise<User> {
// 实现...
return null as any;
}
async update(id: string, user: Partial<User>): Promise<User> {
// 实现...
return null as any;
}
async delete(id: string): Promise<void> {
// 实现...
}
}
工具类型
1. 内置工具类型
// Partial - 使所有属性可选
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
// Record - 创建键值对类型
type PageInfo = {
title: string;
url: string;
}
const pages: Record<string, PageInfo> = {
home: { title: "Home", url: "/" },
about: { title: "About", url: "/about" }
};
2. 自定义工具类型
// 移除只读属性
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
// 深度 Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 类型过滤
type FilterString<T> = {
[P in keyof T]: T[P] extends string ? P : never;
}[keyof T];
最佳实践
1. 命名约定
// 常用泛型参数命名
T - Type
K - Key
V - Value
E - Element
P - Property
R - Return Type
S, U, V etc. - 其他类型
2. 默认类型
// 提供默认类型参数
interface ApiResponse<T = any> {
data: T;
status: number;
}
// 使用
const response: ApiResponse = { data: "hello", status: 200 };
const typedResponse: ApiResponse<number> = { data: 42, status: 200 };
3. 条件类型
// 根据条件选择类型
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
T extends undefined ? "undefined" :
T extends Function ? "function" :
"object";
// 使用
type T0 = TypeName<string>; // "string"
type T1 = TypeName<number>; // "number"
实际应用示例
1. React 组件
interface Props<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: Props<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={index}>{renderItem(item)}</li>
))}
</ul>
);
}
// 使用
<List<string>
items={["a", "b", "c"]}
renderItem={(item) => <span>{item}</span>}
/>
2. 状态管理
class State<T> {
private listeners: ((state: T) => void)[] = [];
private state: T;
constructor(initialState: T) {
this.state = initialState;
}
getState(): T {
return this.state;
}
setState(newState: T): void {
this.state = newState;
this.notify();
}
subscribe(listener: (state: T) => void): () => void {
this.listeners.push(listener);
return () => {
this.listeners = this.listeners.filter(l => l !== listener);
};
}
private notify(): void {
this.listeners.forEach(listener => listener(this.state));
}
}
总结
-
泛型的优点:
- 类型安全
- 代码复用
- 灵活性
- 可维护性
-
使用场景:
- 通用组件
- 数据容器
- API 请求
- 状态管理
-
最佳实践:
- 合理使用约束
- 提供默认类型
- 遵循命名约定
- 保持简单清晰