泛型的意义及语法
定义一个函数或类时,有些情况下无法确定其中要使用的具体类型(返回值、参数、属性的类型不能确定),此时泛型便能够发挥作用。
-
举个例子:
-
function test(arg: any): any{
return arg;
}
-
上例中,test函数有一个参数类型不确定,但是能确定的时其返回值的类型和参数的类型是相同的,由于类型不确定所以参数和返回值均使用了any,但是很明显这样做是不合适的,首先使用any会关闭TS的类型检查,其次这样设置也不能体现出参数和返回值是相同的类型
-
使用泛型:
-
function test<T>(arg: T): T{
return arg;
}
-
这里的<T>
就是泛型,T是我们给这个类型起的名字(不一定非叫T),设置泛型后即可在函数中使用T来表示该类型。所以泛型其实很好理解,就表示某个类型。
-
那么如何使用上边的函数呢?
-
方式一(直接使用):
-
方式二(指定类型):
-
test<number>(10)
-
也可以在函数后手动指定泛型
-
可以同时指定多个泛型,泛型间使用逗号隔开:
-
function test<T, K>(a: T, b: K): K{
return b;
}
test<number, string>(10, "hello");
-
使用泛型时,完全可以将泛型当成是一个普通的类去使用
-
类中同样可以使用泛型:
-
除此之外,也可以对泛型的范围进行约束
复杂例子:可扩展的数据结构
现在让我们看一个更复杂的例子,使用泛型创建一个可扩展的数据结构。假设我们要实现一个Stack(栈)数据结构,它可以存储不同类型的元素。下面是使用泛型实现的Stack类:
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
// 使用泛型类
const stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.push(3);
while (!stack.isEmpty()) {
console.log(stack.pop()); // 输出:3, 2, 1
}
在上面的例子中,我们使用泛型类Stack来创建一个可以存储不同类型元素的栈。通过使用泛型,我们可以在创建Stack实例时指定具体的元素类型。这使得我们可以在同一个栈中存储不同类型的数据,并保持类型安全。
例子出处