# 泛型
在本教程中,你将学习 TypeScript 中的泛型,它允许把类型作为形参来使用。
# TypeScript 中的泛型介绍
TypeScript 中的泛型可以编写可重用的泛型 函数,泛型 类 和 泛型 接口,在本教程中,你将专注于开发通用函数。
下面通过一个简单的例子解释 TypeScript 中的泛型的概念。
假设需要开发一个函数,它随机返回一个 数字 数组 中的元素。
下面的 getRandomNumberElement()
函数接受一个数字数组作为参数,并随机返回数组中的一个元素:
function getRandomNumberElement(items: number[]): number {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}
2
3
4
为了随机获取数组中的一个元素,你需要:
- 先找到随机索引;
- 根据随机索引获取随机元素。
要找到数组的随机索引,使用 Math.random()
函数,它返回 0
和 1
之间的随机数,将得到的随机数和数组的长度相乘,得到的结果传递给 Math.floor()
函数,得到随机索引。
下面演示如何使用 getRandomNumberElement()
函数:
let numbers = [1, 5, 7, 4, 2, 9];
console.log(getRandomNumberElement(numbers));
2
假设你需要从一个 字符串 数组中随机获取一个元素,你可能想到开发一个新的函数:
function getRandomStringElement(items: string[]): string {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}
2
3
4
getRandomStringElement()
函数的逻辑和 getRandomNumberElement()
函数的逻辑相同。
下面演示如何使用 getRandomStringElement()
函数:
let colors = ['red', 'green', 'blue'];
console.log(getRandomStringElement(colors));
2
稍后你可能需要随机获取对象数组中的一个元素,每次想从新的类型数组中随机获得一个元素的时候,都需要创建一个新的函数,这种方式不具有可扩展性。
# 使用 any 类型
这个问题的一个解决方案是把数组参数的类型设置为 any[]
类型,这样处理后只需要编写一个用于任何类型的数组的函数:
function getRandomAnyElement(items: any[]): any {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}
2
3
4
getRandomAnyElement()
函数适用于 any
类型的数组,包括数字,字符串和对象类型等等。
let numbers = [1, 5, 7, 4, 2, 9];
let colors = ['red', 'green', 'blue'];
console.log(getRandomAnyElement(numbers));
console.log(getRandomAnyElement(colors));
2
3
4
5
这个解决方法是有效的,但是它有一个缺点:无法强制指定返回元素的类型,换句话说,它不是类型安全的。想要在保留类型的同时避免重复的代码,使用泛型是一个很好的解决方案。
# TypeScript 中的泛型可以帮上忙
下面是一个泛型函数,它从类型为 T
的数组中随机返回一个元素:
function getRandomElement<T>(items: T[]): T {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}
2
3
4
这个函数使用类型变量 T
,T
允许你捕获调用函数的时候提供的类型,此外,该函数使用 T
类型作为函数的返回类型。getRandomElement()
函数是通用的,因为它可以处理任何数据类型的数组,包括字符串,数字和对象类型等等。
按照惯例,我们使用 T
作为类型变量,当然你也可以使用其他字母,比如 A
,B
和 C
等等。
# 调用泛型函数
下面演示如何使用数字数组来调用 getRandomElement()
函数:
let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement<number>(numbers);
console.log(randomEle);
2
3
这个例子显式地把 T
类型对象赋值为 number
,传递给 getRandomElement()
函数。
实践中,你可以使用 类型推断 来推断 T
类型对象的类型。这意味着你可以让 TypeScript 编译器根据你传递的参数自动设置 T
的值,如下所示:
let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement(numbers);
console.log(randomEle);
2
3
在这个例子中,我们没有显式地给 getRandomElement()
函数传递 数字类型,编译器会把 T
设置为对应的类型。现在 getRandomElement()
函数也是类型安全的了,如果把返回值赋值给一个字符串变量,将会得到一个错误提示:
let numbers = [1, 5, 7, 4, 2, 9];
let returnElem: string;
returnElem = getRandomElement(numbers); // compiler error
2
3
# 具有多个类型变量的泛型函数
下面演示如何使用两个类型变量 U
和 V
来开发泛型函数:
function merge<U, V>(obj1: U, obj2: V) {
return {
...obj1,
...obj2,
};
}
2
3
4
5
6
merge()
函数合并类型为 U
和 V
的两个对象,将它们的属性组合成一个新的对象。 merge()
函数的返回类型被推断为 U
和 V
的交集类型,即 U & V
。
下面演示了如何使用 merge()
函数来合并两个对象:
let result = merge({ name: 'John' }, { jobTitle: 'Frontend Developer' });
console.log(result);
2
3
输出:
{ name: 'John', jobTitle: 'Frontend Developer' }
# TypeScript 中泛型的作用
下面是 TypeScript 中泛型的作用:
- 编译时进行类型检查;
- 消除 类型转换;
- 实现泛型算法。
# 小结
- 使用 TypeScript 泛型可以开发可复用的,具有通用性的和类型安全的函数,接口或者类。