在前面章节的学习中,无论是定义变量、类还是接口,我们都在做一件事情:把类型写死。比如使用了 name: string,那 name 就绝对不能是 number 类型。
这种极其严格的 “写死” 机制虽然保证了类型的安全,但在某些场景下,却极大地扼杀了代码的 “复用性”。
TypeScript 为什么需要泛型?
假设我们现在是一个底层工具库的开发者,需要写一个非常简单的函数:我们传给它什么参数,它就原封不动地返回什么结果。
如果使用以前学过的知识来写这个 echo() 函数,大概会写成下面这样:
function echo(arg: number): number {
return arg;
}那么问题就来了:现在这个函数只能接收并返回数字。如果业务小弟想要传入一个字符串,或者传入一个包含用户信息的对象,那么这个函数就直接废了。
为了让这个函数能够接收所有类型,很多大聪明小伙伴会很自然地祭出臭名昭著的 “Any 大法”:
function echo(arg: any): any {
return arg;
}
const result = echo("Jack");分析:
虽然这段代码不报错了,但却引发了一个致命的后果——TypeScript 类型推断彻底丢失。
当传入字符串 "Jack" 时,我们期望 result 是一个 string 类型,以便后续可以调用 result.length。但是,由于返回值被写死了成 any,TypeScript 编译器完全不知道 result 到底是个什么东西,因此后续代码中将失去所有的智能提示和安全检查。
TypeScript 泛型究竟是什么?
为了完美解决这个 “既要复用代码、又要保留类型推断” 的千古难题,TypeScript 引入了 “泛型(Generics)” 的概念。
什么是泛型呢?用一句话来解释就是:泛型就是专门用来装 “类型” 的变量。
以前我们定义的普通变量,里面装的是具体的值(比如 666、true、"绿叶网" 等)。而泛型变量,里面装的是具体的类型(比如 string、number 等)。
在 TypeScript 中,我们可以用 “<T>” 来声明一个泛型变量。其中,使用了泛型变量的函数,也叫做 “泛型函数”。
语法:
function 函数名<泛型变量名>(参数名: 泛型变量名): 泛型变量名 {
// 函数体逻辑
}说明:
这里的 “T” 是 Type(类型)的首字母(非常重要)。
示例 1:使用泛型重写 echo() 函数
function echo<T>(arg: T): T {
return arg;
}
// 传入数字
const numResult = echo<number>(2026);
console.log(numResult);
// 传入字符串
const strResult = echo<string>("绿叶网");
console.log(strResult);运行结果如下。
2026
绿叶网分析:
在函数名后面加上 “<T>”,这就相当于声明了一个类型变量 “T”。然后我们就可以在参数类型和返回值类型中,使用这个 “T” 了。
需要注意的是,我们必须先在函数名后面加上 “<T>”,然后才能在 “参数类型” 和 “返回值类型” 中使用这个 “T”。
// 正确
function echo<T>(arg: T): T {
return arg;
}
// 错误
function echo(arg: T): T {
return arg;
}在这个例子中,当我们调用 echo<string>("绿叶网") 时,就相当于把 string 这个 “类型” 赋值给了泛型变量 “T”。此时,这个 echo 函数在 TypeScript 编译器的眼里,就完全等价于:
function echo(arg: string): string {
return arg;
}TypeScript 泛型的类型推断
在上面的例子中,我们调用函数时手动传入了类型,比如 echo<number>(2026) 和 echo<string>("绿叶网")。
但在实际开发中,程序员都是比较 “懒” 的。我们可以借助 TypeScript 强大的类型推断能力,让编译器可以根据我们传入的实际参数,自动推导出泛型变量 “T” 的类型。
示例 2:利用类型推断简化
function echo<T>(arg: T): T {
return arg;
}
let numResult = echo(2026);
console.log(numResult);
let strResult = echo("绿叶网");
console.log(strResult);运行结果如下。
2026
绿叶网分析:
在这个例子中,我们省略了 <string> 和 <number>,让 TypeScript 自动推断类型。当我们写出 echo("绿叶网") 时,TypeScript 会根据传入的实际参数,自动推导出与之对应的类型。这样不仅省去了手动写类型的麻烦。
TypeScript 泛型字母的 “黑话” 指南
虽然我们可以使用任何字母或单词来命名泛型(比如 <MyType>),但在阅读 Vue 或 React 的底层源码时,我们会发现大佬们都非常喜欢使用单字母。这其实也是业界约定俗成的 “黑话”:
- T (Type):代表通用的 “类型”(非常重要)。
- K (Key):代表对象中的 “键” 的类型。
- V (Value):代表对象中的 “值” 的类型。
- E (Element):代表数组或集合中的 “元素” 类型。
- U, S:通常紧跟在 T 后面,代表第 2 种、第 3 种类型。
掌握这套黑话,以后看各种框架的源码,我们就能一眼洞穿架构师的意图了。
TypeScript 泛型要学习哪些内容?
泛型是 TypeScript 极其重要的概念之一,在这一章中,我们需要学习以下内容:
