Appearance
TypeScript 泛型 Generic
泛型是 TypeScript 中非常重要的高级特性,它允许你创建可重用的组件,这些组件可以适用于多种类型而不是单一类型。本章节将详细介绍 TypeScript 泛型的使用。
什么是泛型?为什么要用?
什么是泛型?
泛型(Generics)是一种允许你在定义函数、接口或类时使用类型参数的特性。这些类型参数可以在使用时被具体的类型所替换。
为什么要用泛型?
- 代码复用:泛型允许你编写可重用的代码,适用于多种类型
- 类型安全:泛型可以在编译时提供类型检查,避免运行时错误
- 灵活性:泛型使代码更加灵活,可以适应不同的类型需求
- 可读性:泛型可以使代码更加清晰,明确表达代码的意图
泛型函数
基本泛型函数
示例:
typescript
// 泛型函数
function identity<T>(value: T): T {
return value;
}
// 使用泛型函数
const numberValue = identity<number>(42); // 类型为 number
const stringValue = identity<string>("Hello"); // 类型为 string
const booleanValue = identity<boolean>(true); // 类型为 boolean
// 类型推断
const inferredValue = identity("TypeScript"); // 类型被推断为 string多个类型参数
示例:
typescript
// 多个类型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// 使用
const numberAndString = pair<number, string>(42, "Hello"); // 类型为 [number, string]
const booleanAndNumber = pair<boolean, number>(true, 100); // 类型为 [boolean, number]泛型接口
基本泛型接口
示例:
typescript
// 泛型接口
interface Container<T> {
value: T;
getValue(): T;
}
// 实现泛型接口
class NumberContainer implements Container<number> {
value: number;
constructor(value: number) {
this.value = value;
}
getValue(): number {
return this.value;
}
}
class StringContainer implements Container<string> {
value: string;
constructor(value: string) {
this.value = value;
}
getValue(): string {
return this.value;
}
}
// 使用
const numberContainer = new NumberContainer(42);
console.log(numberContainer.getValue()); // 42
const stringContainer = new StringContainer("Hello");
console.log(stringContainer.getValue()); // Hello泛型函数接口
示例:
typescript
// 泛型函数接口
interface Mapper<T, U> {
(value: T): U;
}
// 使用泛型函数接口
const numberToString: Mapper<number, string> = (value) => value.toString();
const stringToNumber: Mapper<string, number> = (value) => parseInt(value);
console.log(numberToString(42)); // "42"
console.log(stringToNumber("123")); // 123泛型类
示例:
typescript
// 泛型类
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;
}
size(): number {
return this.items.length;
}
}
// 使用
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop()); // 3
const stringStack = new Stack<string>();
stringStack.push("a");
stringStack.push("b");
stringStack.push("c");
console.log(stringStack.pop()); // "c"泛型约束
基本约束
示例:
typescript
// 定义一个有 length 属性的接口
interface Lengthwise {
length: number;
}
// 泛型约束
function logLength<T extends Lengthwise>(value: T): void {
console.log(`Length: ${value.length}`);
}
// 使用
logLength("Hello"); // Length: 5
logLength([1, 2, 3]); // Length: 3
logLength({ length: 10 }); // Length: 10
// 错误:number 类型没有 length 属性
// logLength(42); // 错误:Argument of type 'number' is not assignable to parameter of type 'Lengthwise'多个约束
示例:
typescript
// 定义多个接口
interface Lengthwise {
length: number;
}
interface HasName {
name: string;
}
// 多个约束
function process<T extends Lengthwise & HasName>(value: T): void {
console.log(`Name: ${value.name}, Length: ${value.length}`);
}
// 使用
process({ name: "Test", length: 10 }); // Name: Test, Length: 10
// 错误:缺少 name 属性
// process({ length: 10 }); // 错误:Argument of type '{ length: number; }' is not assignable to parameter of type 'Lengthwise & HasName'
// 错误:缺少 length 属性
// process({ name: "Test" }); // 错误:Argument of type '{ name: string; }' is not assignable to parameter of type 'Lengthwise & HasName'默认泛型类型
示例:
typescript
// 默认泛型类型
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
// 使用
const stringArray = createArray(3, "Hello"); // 类型为 string[]
const numberArray = createArray<number>(3, 42); // 类型为 number[]
const booleanArray = createArray<boolean>(3, true); // 类型为 boolean[]实战:封装通用工具函数
示例 1:通用缓存工具
typescript
// 泛型缓存接口
interface Cache<T> {
set(key: string, value: T): void;
get(key: string): T | undefined;
remove(key: string): void;
clear(): void;
}
// 实现泛型缓存类
class MemoryCache<T> implements Cache<T> {
private cache: Map<string, T> = new Map();
set(key: string, value: T): void {
this.cache.set(key, value);
}
get(key: string): T | undefined {
return this.cache.get(key);
}
remove(key: string): void {
this.cache.delete(key);
}
clear(): void {
this.cache.clear();
}
}
// 使用
const userCache = new MemoryCache<{ id: number; name: string }>();
userCache.set("user1", { id: 1, name: "John" });
const user = userCache.get("user1");
console.log(user); // { id: 1, name: "John" }
const productCache = new MemoryCache<{ id: number; name: string; price: number }>();
productCache.set("product1", { id: 1, name: "Product 1", price: 99.99 });
const product = productCache.get("product1");
console.log(product); // { id: 1, name: "Product 1", price: 99.99 }示例 2:通用 API 响应处理
typescript
// 泛型 API 响应接口
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: {
code: number;
message: string;
};
}
// 泛型 API 客户端
class ApiClient {
async get<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
const data = await response.json();
return data;
}
async post<T>(url: string, body: any): Promise<ApiResponse<T>> {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});
const data = await response.json();
return data;
}
}
// 定义数据类型
interface User {
id: number;
name: string;
email: string;
}
interface Product {
id: number;
name: string;
price: number;
}
// 使用
const apiClient = new ApiClient();
// 获取用户
async function fetchUser() {
const response = await apiClient.get<User>("/api/users/1");
if (response.success && response.data) {
console.log(response.data.name);
}
}
// 获取产品
async function fetchProduct() {
const response = await apiClient.get<Product>("/api/products/1");
if (response.success && response.data) {
console.log(response.data.price);
}
}小结
本章节介绍了 TypeScript 泛型的使用,包括:
- 什么是泛型?为什么要用?
- 泛型函数
- 泛型接口
- 泛型类
- 泛型约束
- 默认泛型类型
- 实战:封装通用工具函数
泛型是 TypeScript 中非常强大的特性,它可以帮助你创建可重用、类型安全的代码。通过使用泛型,你可以编写更加灵活、可维护的代码,特别是在处理不同类型数据的场景中。
在实际开发中,应该充分利用泛型来提高代码的复用性和类型安全性,特别是在开发通用工具、组件和库时。
