TS中的泛型是啥玩意?详解一下!
2023-10-29 09:23:03
前言
关于TS我们介绍已经有许多了,这节我们来看泛型。关于之前其他内容的介绍可以自行移步:
- 一分钟看完,TS中的类的继承以及多态
- 一分钟看完,TS中的类、接口和类型别名
- 一分钟看完,TS中的修饰符
- 一分钟看完,TS中的联合类型和元组类型
- 一分钟看完,TS中的映射类型和交叉类型
- 一分钟看完,TS中的类以及它的成员
正文
一、什么是泛型
泛型是一种在编写代码时使用参数化类型的方法,以便能够在一个地方定义代码,而无需为每种可能的数据类型都编写单独的代码。泛型允许我们创建可重用的代码组件,这些组件可以与任何类型的数据一起使用。
二、泛型函数
泛型函数是使用泛型类型参数定义的函数。泛型函数可以接受任何类型的数据作为参数,并返回任何类型的数据。
例如,我们可以定义一个泛型函数swap
来交换两个变量的值:
function swap<T>(a: T, b: T): void {
let temp = a;
a = b;
b = temp;
}
这个函数使用了一个泛型类型参数T
,表示要交换的变量的类型。我们可以用这个函数来交换任何类型的数据,例如:
swap(1, 2); // 交换两个数字
swap("a", "b"); // 交换两个字符串
swap([1, 2, 3], [4, 5, 6]); // 交换两个数组
三、泛型类
泛型类是使用泛型类型参数定义的类。泛型类可以创建具有特定类型参数的对象。
例如,我们可以定义一个泛型类Stack
来表示栈:
class Stack<T> {
private data: T[] = [];
push(item: T) {
this.data.push(item);
}
pop(): T | undefined {
return this.data.pop();
}
peek(): T | undefined {
return this.data[this.data.length - 1];
}
isEmpty(): boolean {
return this.data.length === 0;
}
}
这个类使用了一个泛型类型参数T
,表示栈中元素的类型。我们可以用这个类来创建具有任何类型元素的栈,例如:
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("a");
stringStack.push("b");
stringStack.push("c");
console.log(stringStack.pop()); // c
console.log(stringStack.peek()); // b
四、泛型接口
泛型接口是使用泛型类型参数定义的接口。泛型接口可以定义具有特定类型参数的方法和属性。
例如,我们可以定义一个泛型接口Comparator
来表示比较函数:
interface Comparator<T> {
(a: T, b: T): number;
}
这个接口使用了一个泛型类型参数T
,表示要比较的元素的类型。我们可以用这个接口来定义比较函数,例如:
const numberComparator: Comparator<number> = (a, b) => a - b;
const stringComparator: Comparator<string> = (a, b) => a.localeCompare(b);
这些比较函数可以用于对任何类型的数据进行排序,例如:
const numbers = [1, 2, 3, 4, 5];
numbers.sort(numberComparator);
console.log(numbers); // [1, 2, 3, 4, 5]
const strings = ["a", "b", "c", "d", "e"];
strings.sort(stringComparator);
console.log(strings); // ["a", "b", "c", "d", "e"]
五、泛型约束
泛型约束允许我们在定义泛型类型参数时指定一些限制条件。这可以确保泛型类型参数只能是满足这些限制条件的类型。
例如,我们可以定义一个泛型类Queue
来表示队列,并使用泛型约束来确保队列中的元素只能是实现了Comparable
接口的类型:
interface Comparable<T> {
compareTo(other: T): number;
}
class Queue<T extends Comparable<T>> {
private data: T[] = [];
enqueue(item: T) {
this.data.push(item);
}
dequeue(): T | undefined {
return this.data.shift();
}
peek(): T | undefined {
return this.data[0];
}
isEmpty(): boolean {
return this.data.length === 0;
}
}
这个类使用了一个泛型类型参数T
,表示队列中元素的类型。我们还使用了一个泛型约束T extends Comparable<T>
来确保T
类型必须实现了Comparable
接口。这意味着我们可以用这个类来创建具有任何类型元素的队列,只要这些元素实现了Comparable
接口,例如:
class Number implements Comparable<Number> {
private value: number;
constructor(value: number) {
this.value = value;
}
compareTo(other: Number): number {
return this.value - other.value;
}
}
const numberQueue = new Queue<Number>();
numberQueue.enqueue(new Number(1));
numberQueue.enqueue(new Number(2));
numberQueue.enqueue(new Number(3));
console.log(numberQueue.dequeue()); // Number { value: 1 }
console.log(numberQueue.peek()); // Number { value: 2 }
class String implements Comparable<String> {
private value: string;
constructor(value: string) {
this.value = value;
}
compareTo(other: String): number {
return this.value.localeCompare(other.value);
}
}
const stringQueue = new Queue<String>();
stringQueue.enqueue(new String("a"));
stringQueue.enqueue(new String("b"));
stringQueue.enqueue(new String("c"));
console.log(stringQueue.dequeue()); // String { value: "a" }
console.log(stringQueue.peek()); // String { value: "b" }
六、泛型工厂函数
泛型工厂函数是使用泛型类型参数定义的函数,该函数返回一个具有特定类型参数的实例。
例如,我们可以定义一个泛型工厂函数createStack
来创建一个具有特定类型参数的栈:
function createStack<T>(): Stack<T> {
return new Stack<T>();
}
这个函数使用了一个泛型类型参数T
,表示栈中元素的类型。我们可以用这个函数来创建具有任何类型元素的栈,例如:
const numberStack = createStack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.push(3);
console.log(numberStack.pop()); // 3
console.log(numberStack.peek()); // 2
const stringStack = createStack<string>();
stringStack.push("a");
stringStack.push("b");
stringStack.push("c");
console.log(stringStack.pop()); // c
console.log(stringStack.peek()); // b
七、泛型方法
泛型方法是使用泛型类型参数定义的类或接口的方法。泛型方法可以接受任何类型的数据作为参数,并返回任何类型的数据。
例如,我们可以定义一个泛型类List
来表示列表,并使用泛型方法map
来将列表中的每个元素映射到一个新的元素:
class List<T> {
private data: T[] = [];
map<U>(f: (item: T) => U): List<U> {
const result = new List<U>();
for (const item of this.data