Appearance
TypeScript 泛型
在 TypeScript 中,泛型(Generics)是一种强大的特性,它允许我们编写可重用的代码,可以适用于多种类型,而不是单一类型。泛型可以提高代码的灵活性和可重用性,同时保持类型安全。本文将详细介绍 TypeScript 中的泛型。
1. 泛型的基本概念
什么是泛型?
泛型是一种参数化类型的机制,它允许我们在定义函数、类、接口时使用类型参数,而不是具体的类型。这样,我们可以编写适用于多种类型的代码,而不需要为每种类型编写重复的代码。
为什么使用泛型?
- 代码重用:泛型允许我们编写可重用的代码,可以适用于多种类型。
- 类型安全:泛型在编译时进行类型检查,确保类型安全。
- 灵活性:泛型使代码更加灵活,可以处理不同类型的数据。
2. 泛型函数
基本用法
使用尖括号(<>)定义泛型参数,然后在函数参数和返回值中使用这些参数。
typescript
// 泛型函数
function identity<T>(value: T): T {
return value;
}
// 使用泛型函数
const number = identity<number>(42); // 类型为 number
const string = identity<string>("Hello"); // 类型为 string
const boolean = identity<boolean>(true); // 类型为 boolean
// 类型推断
const number2 = identity(42); // 类型为 number
const string2 = identity("Hello"); // 类型为 string多个泛型参数
函数可以有多个泛型参数。
typescript
// 多个泛型参数
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// 使用
const pair1 = pair<number, string>(1, "one"); // 类型为 [number, string]
const pair2 = pair<string, boolean>("hello", true); // 类型为 [string, boolean]
// 类型推断
const pair3 = pair(2, "two"); // 类型为 [number, string]泛型约束
使用 extends 关键字对泛型参数进行约束。
typescript
// 泛型约束
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(value: T): void {
console.log(`Length: ${value.length}`);
}
// 正确:string 有 length 属性
logLength("Hello"); // 输出:Length: 5
// 正确:array 有 length 属性
logLength([1, 2, 3]); // 输出:Length: 3
// 错误:number 没有 length 属性
// logLength(42); // 类型 'number' 不能赋值给类型 'Lengthwise'泛型参数默认类型
TypeScript 2.3+ 支持泛型参数默认类型。
typescript
// 泛型参数默认类型
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
// 使用默认类型
const strings = createArray(3, "hello"); // 类型为 string[]
// 指定类型
const numbers = createArray<number>(3, 42); // 类型为 number[]
const booleans = createArray<boolean>(3, true); // 类型为 boolean[]3. 泛型类
基本用法
使用尖括号(<>)定义泛型参数,然后在类的属性、方法中使用这些参数。
typescript
// 泛型类
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>(42);
console.log(numberContainer.getValue()); // 输出:42
numberContainer.setValue(100);
console.log(numberContainer.getValue()); // 输出:100
const stringContainer = new Container<string>("Hello");
console.log(stringContainer.getValue()); // 输出:Hello
stringContainer.setValue("World");
console.log(stringContainer.getValue()); // 输出:World多个泛型参数
类可以有多个泛型参数。
typescript
// 多个泛型参数
class Pair<T, U> {
private first: T;
private second: U;
constructor(first: T, second: U) {
this.first = first;
this.second = second;
}
getFirst(): T {
return this.first;
}
getSecond(): U {
return this.second;
}
setFirst(value: T): void {
this.first = value;
}
setSecond(value: U): void {
this.second = value;
}
}
// 使用
const pair = new Pair<number, string>(1, "one");
console.log(pair.getFirst()); // 输出:1
console.log(pair.getSecond()); // 输出:one泛型约束
类的泛型参数也可以使用约束。
typescript
// 泛型约束
interface HasId {
id: number;
}
class Repository<T extends HasId> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
findById(id: number): T | undefined {
return this.items.find(item => item.id === id);
}
getAll(): T[] {
return this.items;
}
}
// 定义符合约束的类型
interface User extends HasId {
name: string;
email: string;
}
// 使用
const userRepository = new Repository<User>();
userRepository.add({ id: 1, name: "John", email: "john@example.com" });
userRepository.add({ id: 2, name: "Jane", email: "jane@example.com" });
const user = userRepository.findById(1);
console.log(user); // 输出:{ id: 1, name: 'John', email: 'john@example.com' }
const allUsers = userRepository.getAll();
console.log(allUsers); // 输出:所有用户泛型参数默认类型
类的泛型参数也可以有默认类型。
typescript
// 泛型参数默认类型
class Storage<T = string> {
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<number>();
numberStorage.add(1);
numberStorage.add(2);
console.log(numberStorage.getAll()); // 输出:[1, 2]4. 泛型接口
基本用法
使用尖括号(<>)定义泛型参数,然后在接口中使用这些参数。
typescript
// 泛型接口
interface Result<T> {
success: boolean;
data: T;
message?: string;
}
// 使用泛型接口
const successResult: Result<number> = {
success: true,
data: 42
};
const errorResult: Result<string> = {
success: false,
data: "",
message: "Error occurred"
};
// 泛型函数使用泛型接口
function createResult<T>(success: boolean, data: T, message?: string): Result<T> {
return {
success,
data,
message
};
}
const result1 = createResult<number>(true, 100);
const result2 = createResult<string>(false, "", "Failed");泛型接口作为函数类型
泛型接口可以作为函数类型。
typescript
// 泛型接口作为函数类型
interface Comparator<T> {
(a: T, b: T): number;
}
// 使用
const numberComparator: Comparator<number> = (a, b) => a - b;
const stringComparator: Comparator<string> = (a, b) => a.localeCompare(b);
console.log(numberComparator(5, 3)); // 输出:2
console.log(stringComparator("apple", "banana")); // 输出:-1泛型接口继承
泛型接口可以继承其他泛型接口。
typescript
// 泛型接口继承
interface Collection<T> {
add(item: T): void;
remove(item: T): void;
size(): number;
}
interface List<T> extends Collection<T> {
get(index: number): T;
set(index: number, item: T): void;
}
// 实现泛型接口
class ArrayList<T> implements List<T> {
private items: T[] = [];
add(item: T): void {
this.items.push(item);
}
remove(item: T): void {
const index = this.items.indexOf(item);
if (index !== -1) {
this.items.splice(index, 1);
}
}
size(): number {
return this.items.length;
}
get(index: number): T {
return this.items[index];
}
set(index: number, item: T): void {
this.items[index] = item;
}
}
// 使用
const list = new ArrayList<number>();
list.add(1);
list.add(2);
list.add(3);
console.log(list.size()); // 输出:3
console.log(list.get(0)); // 输出:1
list.set(0, 10);
console.log(list.get(0)); // 输出:10
list.remove(2);
console.log(list.size()); // 输出:25. 泛型约束
基本约束
使用 extends 关键字对泛型参数进行约束。
typescript
// 基本约束
interface HasName {
name: string;
}
function printName<T extends HasName>(obj: T): void {
console.log(obj.name);
}
// 正确:符合约束
printName({ name: "John", age: 30 }); // 输出:John
// 错误:不符合约束
// printName({ age: 30 }); // 类型 '{ age: number; }' 不能赋值给类型 'HasName'多重约束
泛型参数可以有多个约束,使用 & 符号。
typescript
// 多重约束
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
function printInfo<T extends HasName & HasAge>(obj: T): void {
console.log(`Name: ${obj.name}, Age: ${obj.age}`);
}
// 正确:符合所有约束
printName({ name: "John", age: 30 }); // 输出:Name: John, Age: 30
// 错误:缺少 age 属性
// printName({ name: "John" }); // 类型 '{ name: string; }' 不能赋值给类型 'HasName & HasAge'
// 错误:缺少 name 属性
// printName({ age: 30 }); // 类型 '{ age: number; }' 不能赋值给类型 'HasName & HasAge'约束为类
泛型参数可以约束为某个类。
typescript
// 约束为类
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
makeSound(): void {
console.log("Some sound");
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
makeSound(): void {
console.log("Woof! Woof!");
}
}
class Cat extends Animal {
constructor(name: string) {
super(name);
}
makeSound(): void {
console.log("Meow! Meow!");
}
}
function animalSound<T extends Animal>(animal: T): void {
console.log(`${animal.name} says:`);
animal.makeSound();
}
// 正确:Dog 是 Animal 的子类
animalSound(new Dog("Rex", "Labrador")); // 输出:Rex says: Woof! Woof!
// 正确:Cat 是 Animal 的子类
animalSound(new Cat("Whiskers")); // 输出:Whiskers says: Meow! Meow!
// 错误:不是 Animal 的子类
// animalSound({ name: "John" }); // 类型 '{ name: string; }' 不能赋值给类型 'Animal'6. 泛型工具类型
TypeScript 提供了一些内置的泛型工具类型,用于常见的类型转换操作。
Partial<T>
将类型 T 的所有属性变为可选。
typescript
// Partial<T>
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 所有属性变为可选
const partialUser: Partial<User> = {
name: "John",
email: "john@example.com"
};Required<T>
将类型 T 的所有属性变为必选。
typescript
// Required<T>
interface User {
id: number;
name: string;
email?: string;
age?: number;
}
// 所有属性变为必选
const requiredUser: Required<User> = {
id: 1,
name: "John",
email: "john@example.com",
age: 30
};Readonly<T>
将类型 T 的所有属性变为只读。
typescript
// Readonly<T>
interface User {
id: number;
name: string;
email: string;
}
// 所有属性变为只读
const readonlyUser: Readonly<User> = {
id: 1,
name: "John",
email: "john@example.com"
};
// 错误:不能修改只读属性
// readonlyUser.name = "Jane"; // 无法分配到 "name" ,因为它是只读属性Record<K, T>
创建一个类型,其中键的类型为 K,值的类型为 T。
typescript
// Record<K, T>
const users: Record<number, { name: string; email: string }> = {
1: { name: "John", email: "john@example.com" },
2: { name: "Jane", email: "jane@example.com" },
3: { name: "Bob", email: "bob@example.com" }
};
// 使用字符串作为键
const userRoles: Record<string, string> = {
"john": "admin",
"jane": "user",
"bob": "user"
};Pick<T, K>
从类型 T 中选取属性 K。
typescript
// Pick<T, K>
interface User {
id: number;
name: string;
email: string;
age: number;
role: string;
}
// 只选取 name 和 email 属性
const userInfo: Pick<User, "name" | "email"> = {
name: "John",
email: "john@example.com"
};Omit<T, K>
从类型 T 中排除属性 K。
typescript
// Omit<T, K>
interface User {
id: number;
name: string;
email: string;
age: number;
role: string;
}
// 排除 age 和 role 属性
const userInfo: Omit<User, "age" | "role"> = {
id: 1,
name: "John",
email: "john@example.com"
};Exclude<T, U>
从类型 T 中排除可以赋值给类型 U 的类型。
typescript
// Exclude<T, U>
type T = string | number | boolean;
type U = string | boolean;
type Excluded = Exclude<T, U>; // 类型为 number
// 使用
const value: Excluded = 42; // 正确
// const value: Excluded = "hello"; // 错误:类型 'string' 不能赋值给类型 'number'Extract<T, U>
从类型 T 中提取可以赋值给类型 U 的类型。
typescript
// Extract<T, U>
type T = string | number | boolean;
type U = string | boolean;
type Extracted = Extract<T, U>; // 类型为 string | boolean
// 使用
const value1: Extracted = "hello"; // 正确
const value2: Extracted = true; // 正确
// const value3: Extracted = 42; // 错误:类型 'number' 不能赋值给类型 'string | boolean'NonNullable<T>
从类型 T 中排除 null 和 undefined。
typescript
// NonNullable<T>
type T = string | number | null | undefined;
type NonNull = NonNullable<T>; // 类型为 string | number
// 使用
const value1: NonNull = "hello"; // 正确
const value2: NonNull = 42; // 正确
// const value3: NonNull = null; // 错误:类型 'null' 不能赋值给类型 'string | number'
// const value4: NonNull = undefined; // 错误:类型 'undefined' 不能赋值给类型 'string | number'Parameters<T>
从函数类型 T 中提取参数类型,返回一个元组类型。
typescript
// Parameters<T>
type Func = (a: number, b: string) => boolean;
type FuncParams = Parameters<Func>; // 类型为 [number, string]
// 使用
const params: FuncParams = [42, "hello"];ReturnType<T>
从函数类型 T 中提取返回类型。
typescript
// ReturnType<T>
type Func = (a: number, b: string) => boolean;
type FuncReturn = ReturnType<Func>; // 类型为 boolean
// 使用
const result: FuncReturn = true;7. 泛型的高级用法
递归泛型
泛型可以递归使用,用于处理嵌套结构。
typescript
// 递归泛型
interface TreeNode<T> {
value: T;
children?: TreeNode<T>[];
}
// 使用
const tree: TreeNode<number> = {
value: 1,
children: [
{
value: 2,
children: [
{ value: 3 },
{ value: 4 }
]
},
{
value: 5,
children: [
{ value: 6 }
]
}
]
};
// 遍历树
function traverse<T>(node: TreeNode<T>): void {
console.log(node.value);
if (node.children) {
node.children.forEach(child => traverse(child));
}
}
traverse(tree); // 输出:1 2 3 4 5 6条件类型
条件类型允许我们根据类型条件选择不同的类型。
typescript
// 条件类型
type IsString<T> = T extends string ? true : false;
// 使用
const isString1: IsString<string> = true; // 正确
const isString2: IsString<number> = false; // 正确
// 更复杂的条件类型
type ExtractArrayType<T> = T extends Array<infer U> ? U : T;
// 使用
type A = ExtractArrayType<string[]>; // 类型为 string
type B = ExtractArrayType<number>; // 类型为 number映射类型
映射类型允许我们基于现有类型创建新类型。
typescript
// 映射类型
interface User {
id: number;
name: string;
email: string;
}
// 映射为可选属性
type OptionalUser = {
[K in keyof User]?: User[K];
};
// 映射为只读属性
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
// 使用
const optionalUser: OptionalUser = {
name: "John"
};
const readonlyUser: ReadonlyUser = {
id: 1,
name: "John",
email: "john@example.com"
};
// 错误:不能修改只读属性
// readonlyUser.name = "Jane"; // 无法分配到 "name" ,因为它是只读属性8. 泛型的最佳实践
1. 使用有意义的泛型参数名
使用有意义的泛型参数名,提高代码的可读性。
typescript
// 推荐:使用有意义的泛型参数名
function processArray<T>(array: T[]): T[] {
// 实现
}
// 不推荐:使用单个字母作为泛型参数名
function processArray<T>(array: T[]): T[] {
// 实现
}
// 更推荐:使用更具体的泛型参数名
function processUserArray<User>(array: User[]): User[] {
// 实现
}2. 使用泛型约束
使用泛型约束来限制泛型参数的类型,提高类型安全性。
typescript
// 推荐:使用泛型约束
interface HasId {
id: number;
}
function findById<T extends HasId>(items: T[], id: number): T | undefined {
return items.find(item => item.id === id);
}
// 不推荐:不使用泛型约束
function findById<T>(items: T[], id: number): T | undefined {
// 可能会导致运行时错误
return items.find((item: any) => item.id === id);
}3. 使用泛型默认类型
对于常用的泛型参数,使用默认类型可以简化代码。
typescript
// 推荐:使用泛型默认类型
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
// 不推荐:不使用泛型默认类型
function createArray<T>(length: number, value: T): T[] {
return Array(length).fill(value);
}4. 合理使用泛型工具类型
使用 TypeScript 提供的泛型工具类型来简化类型定义。
typescript
// 推荐:使用泛型工具类型
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 使用 Pick 提取需要的属性
type UserInfo = Pick<User, "name" | "email">;
// 不推荐:手动定义
interface UserInfo {
name: string;
email: string;
}5. 避免过度使用泛型
虽然泛型很强大,但过度使用会使代码变得复杂。只在需要时使用泛型。
typescript
// 推荐:只在需要时使用泛型
function identity<T>(value: T): T {
return value;
}
// 不推荐:过度使用泛型
function add<T>(a: T, b: T): T {
// 这种情况下,泛型可能不是最佳选择,因为不同类型的加法行为不同
return a + b; // 可能会导致类型错误
}9. 常见错误
1. 泛型参数未使用
泛型参数必须在函数或类中使用,否则会导致编译错误。
typescript
// 错误:泛型参数 T 未使用
// function foo<T>(): void {
// console.log("Hello");
// }
// 正确:使用泛型参数
function foo<T>(value: T): T {
return value;
}2. 泛型约束不满足
当使用泛型约束时,传入的类型必须满足约束条件。
typescript
interface HasName {
name: string;
}
function printName<T extends HasName>(obj: T): void {
console.log(obj.name);
}
// 错误:传入的类型不满足约束
// printName({ age: 30 }); // 类型 '{ age: number; }' 不能赋值给类型 'HasName'
// 正确:传入的类型满足约束
printName({ name: "John", age: 30 });3. 泛型类型推断错误
当 TypeScript 无法推断泛型类型时,需要显式指定类型。
typescript
function createPair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
// 正确:TypeScript 可以推断类型
const pair1 = createPair(1, "one"); // 类型为 [number, string]
// 错误:TypeScript 无法推断类型
// const pair2 = createPair({}, {}); // 类型为 [{}, {}],可能不是预期的类型
// 正确:显式指定类型
interface User {
name: string;
}
interface Product {
id: number;
}
const pair3 = createPair<User, Product>({ name: "John" }, { id: 1 }); // 类型为 [User, Product]4. 泛型方法的 this 指向错误
在泛型类的方法中,要注意 this 的指向。
typescript
class Container<T> {
private value: T;
constructor(value: T) {
this.value = value;
}
// 错误:箭头函数中的 this 不是指向类实例
// getValue = (): T => {
// return this.value;
// };
// 正确:使用普通方法
getValue(): T {
return this.value;
}
}
const container = new Container<number>(42);
console.log(container.getValue()); // 输出:425. 泛型工具类型使用错误
使用泛型工具类型时,要确保类型参数正确。
typescript
interface User {
id: number;
name: string;
email: string;
}
// 错误:Pick 的第二个参数必须是 T 的键
// type UserInfo = Pick<User, "name" | "age">; // 类型 "age" 不能分配给类型 keyof User
// 正确:Pick 的第二个参数必须是 T 的键
Type UserInfo = Pick<User, "name" | "email">;10. 实际应用场景
1. 通用数据结构
使用泛型实现通用数据结构,如栈、队列、链表等。
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
console.log(numberStack.peek()); // 输出:2
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
console.log(stringStack.pop()); // 输出:world2. 通用工具函数
使用泛型实现通用工具函数,如排序、过滤、映射等。
typescript
// 泛型排序函数
function sort<T>(array: T[], comparator: (a: T, b: T) => number): T[] {
return [...array].sort(comparator);
}
// 使用
const numbers = [3, 1, 4, 1, 5, 9, 2, 6];
const sortedNumbers = sort(numbers, (a, b) => a - b);
console.log(sortedNumbers); // 输出:[1, 1, 2, 3, 4, 5, 6, 9]
const strings = ["banana", "apple", "cherry"];
const sortedStrings = sort(strings, (a, b) => a.localeCompare(b));
console.log(sortedStrings); // 输出:['apple', 'banana', 'cherry']
// 泛型过滤函数
function filter<T>(array: T[], predicate: (item: T) => boolean): T[] {
return array.filter(predicate);
}
// 使用
const evenNumbers = filter(numbers, (n) => n % 2 === 0);
console.log(evenNumbers); // 输出:[4, 2, 6]
// 泛型映射函数
function map<T, U>(array: T[], mapper: (item: T) => U): U[] {
return array.map(mapper);
}
// 使用
const squaredNumbers = map(numbers, (n) => n * n);
console.log(squaredNumbers); // 输出:[9, 1, 16, 1, 25, 81, 4, 36]3. 通用服务类
使用泛型实现通用服务类,如 API 服务、存储服务等。
typescript
// 泛型 API 服务
class ApiService<T> {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
async get(id: number): Promise<T> {
const response = await fetch(`${this.baseUrl}/${id}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async getAll(): Promise<T[]> {
const response = await fetch(this.baseUrl);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async create(data: T): Promise<T> {
const response = await fetch(this.baseUrl, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async update(id: number, data: T): Promise<T> {
const response = await fetch(`${this.baseUrl}/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(data)
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
}
async delete(id: number): Promise<void> {
const response = await fetch(`${this.baseUrl}/${id}`, {
method: "DELETE"
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
}
}
// 使用
interface User {
id: number;
name: string;
email: string;
}
const userService = new ApiService<User>("https://api.example.com/users");
// 获取用户
userService.get(1).then(user => {
console.log(user);
});
// 创建用户
userService.create({ id: 0, name: "John", email: "john@example.com" }).then(user => {
console.log(user);
});4. 通用状态管理
使用泛型实现通用状态管理,如 Redux store、Context API 等。
typescript
// 泛型状态管理
interface State<T> {
data: T | null;
isLoading: boolean;
error: string | null;
}
interface Action<T> {
type: string;
payload?: T;
error?: string;
}
function createReducer<T>(initialState: State<T>) {
return (state: State<T> = initialState, action: Action<T>): State<T> => {
switch (action.type) {
case "FETCH_START":
return {
...state,
isLoading: true,
error: null
};
case "FETCH_SUCCESS":
return {
...state,
data: action.payload as T,
isLoading: false,
error: null
};
case "FETCH_ERROR":
return {
...state,
isLoading: false,
error: action.error || "An error occurred"
};
default:
return state;
}
};
}
// 使用
interface User {
id: number;
name: string;
email: string;
}
const initialState: State<User> = {
data: null,
isLoading: false,
error: null
};
const userReducer = createReducer<User>(initialState);
// 测试
let state = userReducer(initialState, { type: "FETCH_START" });
console.log(state); // 加载状态
state = userReducer(state, {
type: "FETCH_SUCCESS",
payload: { id: 1, name: "John", email: "john@example.com" }
});
console.log(state); // 成功状态
state = userReducer(state, {
type: "FETCH_ERROR",
error: "Failed to fetch user"
});
console.log(state); // 错误状态5. 通用组件(React/Vue)
在 React 或 Vue 中使用泛型创建通用组件。
typescript
// React 泛型组件
import React from 'react';
interface ListProps<T> {
items: T[];
renderItem: (item: T, index: number) => React.ReactNode;
keyExtractor: (item: T) => string | number;
}
function List<T>({ items, renderItem, keyExtractor }: ListProps<T>) {
return (
<ul>
{items.map((item, index) => (
<li key={keyExtractor(item)}>
{renderItem(item, index)}
</li>
))}
</ul>
);
}
// 使用
interface User {
id: number;
name: string;
email: string;
}
const users: User[] = [
{ id: 1, name: "John", email: "john@example.com" },
{ id: 2, name: "Jane", email: "jane@example.com" },
{ id: 3, name: "Bob", email: "bob@example.com" }
];
function App() {
return (
<List
items={users}
keyExtractor={(user) => user.id}
renderItem={(user) => (
<div>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
)}
/>
);
}
// Vue 泛型组件
// <template>
// <ul>
// <li v-for="(item, index) in items" :key="keyExtractor(item)">
// <slot name="item" :item="item" :index="index"></slot>
// </li>
// </ul>
// </template>
//
// <script lang="ts">
// import { defineComponent } from 'vue';
//
// export default defineComponent({
// name: 'List',
// props: {
// items: {
// type: Array as () => any[],
// required: true
// },
// keyExtractor: {
// type: Function as () => (item: any) => string | number,
// required: true
// }
// }
// });
// </script>总结
TypeScript 中的泛型是一种强大的特性,它允许我们编写可重用的代码,可以适用于多种类型,而不是单一类型。本文介绍了 TypeScript 泛型的以下内容:
- 泛型的基本概念:什么是泛型,为什么使用泛型
- 泛型函数:基本用法,多个泛型参数,泛型约束,泛型参数默认类型
- 泛型类:基本用法,多个泛型参数,泛型约束,泛型参数默认类型
- 泛型接口:基本用法,泛型接口作为函数类型,泛型接口继承
- 泛型约束:基本约束,多重约束,约束为类
- 泛型工具类型:
Partial<T>,Required<T>,Readonly<T>,Record<K, T>,Pick<T, K>,Omit<T, K>,Exclude<T, U>,Extract<T, U>,NonNullable<T>,Parameters<T>,ReturnType<T> - 泛型的高级用法:递归泛型,条件类型,映射类型
- 泛型的最佳实践:使用有意义的泛型参数名,使用泛型约束,使用泛型默认类型,合理使用泛型工具类型,避免过度使用泛型
- 常见错误:泛型参数未使用,泛型约束不满足,泛型类型推断错误,泛型方法的 this 指向错误,泛型工具类型使用错误
- 实际应用场景:通用数据结构,通用工具函数,通用服务类,通用状态管理,通用组件
通过合理使用泛型,你可以在 TypeScript 中编写更加灵活、可重用、类型安全的代码,提高代码的质量和可维护性。