《TypeScript实战指南》—2.1.4 泛型
2.1.4 泛型
泛型对于纯 JavaScript 程序员而言可能有点难以理解。
泛型用于提升代码的重用性。我们希望自己编写的代码,无论是模块还是组件,不仅能支持当前设计的数据类型,而且也能支持将来的数据类型。这在大型系统中是非常基础且重要的功能。所以我们常常能在各种各样的静态类型语言中看到泛型设计,使得用户可以灵活地选择希望支持的类型,而非局限于某一种类型。
1.泛型函数
下面我们来创建一个使用泛型的例子:hello 函数。这个函数非常简单,直接将输入的参数作为它的返回值。
不过,在这之前,我们不妨来假设一下,如果不使用泛型,会是什么样子。
比如,当我们需要传入一个数字的时候是如下这样:
function hello(arg: number): number {
return arg;
}
但是很不幸,我们的需求很快就变更了,这个时候需要传入一个字符串,就会变成如下这样:
function hello(arg: string): string {
return arg;
}
当然我们也可以使用 any 类型来表达这种混沌感:
function hello(arg: any): any {
return arg;
}
但 any 类型并不能准确地表达返回值与参数必须是相同类型,因为 any 代表任何类型,这使得我们的类型表达开始混乱了。
因此,我们需要一种表达方式来控制函数的参数类型,这就要用到泛型了。
TypeScript 的泛型语法非常主流,与 Java 等静态类型语言的使用方式一致,就像下面这样:
function hello<T>(arg: T): T {
return arg;
}
我们给 hello 函数添加了泛型变量T,T 代表用户即将传入的类型。类型既可以是 number,也可以是 string,而在最后,我们还使用 T 作为返回值的类型。这就达到了返回值和参数类型相同的目的,保持住了函数表达的准确性。
那如何使用泛型函数呢?跟之前的类型断言一样,有两种方法可以选择:
使用熟悉的尖括号方式进行表达:
let output = hello<string>("hello TypeScript");
我们在这里明确地指定T是string类型,并将它作为参数传给函数。当然,如果我们需要使用 number 类型,就直接在尖括号中写入 number 即可。
使用类型推断。TypeScript 的编译器会根据传入的参数类型自动确定 T 的类型。这看上去非常智能,写起来是如下这样的:
let output = hello("hello TypeScript");
你什么都不用做,像平常一样写代码即可。
这个时候编译器会明确地知道 T 的类型就是参数 "hello TypeScript" 的类型 string,同样,返回值类型是 T,也是 string 类型。
如果这个时候你使用的是 VSCode,将光标移至 output 上就会看到 string 的类型提示,如果再移动到 hello 头上,可以看到它的参数类型也写着 string。
类型推断帮助我们精简了代码,也提高了可读性,让代码变得简洁。在后续的章节中,我们还会体会到类型的好处。但凡事皆有例外,在某些特别复杂的情况下,类型推断是会失灵的,那么这个时候就需要我们非常明确地写出 T 的类型。
2.泛型变量
创建hello这样的泛型函数时,编译器要求在函数体中正确地使用类型。这听起来像是一句废话,换言之,你必须把这些参数当成任意类型。
让我们回顾一下 hello 这个函数:
function hello<T>(arg: T): T {
return arg;
}
如果我们这个时候需要使用参数 arg 的长度时,就会出现下面这样尴尬的情况:
function hello<T>(arg: T): T {
console.log(arg.length); // Error: T doesn't have .length
return arg;
}
编译器会非常迅速地进行报错,告诉我们泛型 T 并没有 length 这个属性。这似乎有点不合情理,如果 T 是 string 类型,那它为什么没有 length 属性呢。万一 T 是 number 类型呢?就像木桶原理一样,编译器会选择最糟糕的情况进行处理,T 代表任意类型,那么就一定会有最糟糕的情况。
什么情况下一定会有length 属性呢?可以使用泛型数组来表达这样的情况。由于我们操作的是数组,所以 length 属性一定是存在的,那就可以像普通的数组一样操作它:
function hello<T>(args: T[]): T[] {
console.log(args.length);
return arg;
}
这个时候,我们再使用 hello 函数时,就需要传入一个 T 的数组,也就是一个 string 变量的数组,或者 number 变量的数组,而不是一个单纯的变量。返回值也是同类型的数组。这可以让我们把泛型变量 T 作为数组的一部分属性,而不是作为整体类型,这增加了灵活性。
不只是使用中括号,还可以使用 Array 来表达数组,如下所示:
function hello<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg;
}
TypeScript 同时采用了这样一种在其他语言中非常常见的语法。
- 点赞
- 收藏
- 关注作者
评论(0)