跳到主要内容

TypeScript 泛型

摘要:在本教程中,你将了解 TypeScript 泛型,它允许你使用类型作为形式参数。

TypeScript 泛型简介

TypeScript 泛型允许你编写可重用和通用形式的函数、类和接口。 在本教程中,我们将专注于开发泛型函数。

通过一个简单的例子来解释 TypeScript 泛型会更容易。

假设你需要开发一个返回数字数组中的随机元素的函数。

以下 getRandomNumberElement() 函数采用数字数组作为其参数,并从数组中返回一个随机元素:

function getRandomNumberElement(items: number[]): number {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}

要获取数组的随机元素,你需要:

  • 首先找到随机索引。
  • 根据随机索引获取随机元素。

为了查找数组的随机索引,我们使用 Math.random() 返回 01 之间的随机数,将其乘以数组的长度,并对结果应用 Math.floor()

下面展示了如何使用 getRandomNumberElement() 函数:

let numbers = [1, 5, 7, 4, 2, 9];
console.log(getRandomNumberElement(numbers));

假设你需要从字符串数组中获取随机元素。 这次,你可能会想出一个新函数:

function getRandomStringElement(items: string[]): string {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}

getRandomStringElement() 函数的逻辑与 getRandomNumberElement() 函数中的逻辑相同。

此示例显示了如何使用 getRandomStringElement() 函数:

let colors = ['red', 'green', 'blue'];
console.log(getRandomStringElement(colors));

稍后你可能需要获取对象数组中的随机元素。每次想要从新数组类型中获取随机元素时创建一个新函数的方式是不可扩展的。

使用 any 类型

此问题的一种解决方案是将数组参数的类型设置为 any[]。 通过这样做,你只需编写一个可以处理任何类型数组的函数。

function getRandomAnyElement(items: any[]): any {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}

getRandomAnyElement() 适用于任何类型的数组,包括数字、字符串、对象等数组:

let numbers = [1, 5, 7, 4, 2, 9];
let colors = ['red', 'green', 'blue'];

console.log(getRandomAnyElement(numbers));
console.log(getRandomAnyElement(colors));

这个解决方案效果很好。 然而,它有一个缺点。

它不允许你强制返回元素的类型。 换句话说,它不是类型安全的。

在保留类型的同时避免代码重复的更好解决方案是使用泛型。

TypeScript 泛型

下面显示了一个从类型为 T 的数组中返回随机元素的通用函数:

function getRandomElement<T>(items: T[]): T {
let randomIndex = Math.floor(Math.random() * items.length);
return items[randomIndex];
}

该函数使用了类型变量 TT 允许你捕获调用该函数时提供的类型。 此外,该函数使用 T 类型变量作为其返回类型。

这个 getRandomElement() 函数是通用的,因为它可以处理任何数据类型,包括字符串、数字、对象等等。

按照惯例,我们使用字母 T 作为类型变量。 但是,你可以自由使用其他字母,例如 ABC等。

调用泛型函数

下面显示了如何将 getRandomElement() 与数字数组一起使用:

let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement<number>(numbers);
console.log(randomEle);

此示例显式地将 number 作为 T 类型传递到 getRandomElement() 函数中。

在实践中,你将使用参数的类型推断。 这意味着你可以让 TypeScript 编译器根据传入的参数类型自动设置 T 的值,如下所示:

let numbers = [1, 5, 7, 4, 2, 9];
let randomEle = getRandomElement(numbers);
console.log(randomEle);

在此示例中,我们没有显式地将 number 类型传递给 getRandomElement()。 编译器会查看参数并将 T 设置为其类型。

现在, getRandomElement() 函数也是类型安全的。 例如,如果将返回值分配给字符串变量,则会收到错误:

let numbers = [1, 5, 7, 4, 2, 9];
let returnElem: string;
returnElem = getRandomElement(numbers); // compiler error

具有多种类型的泛型函数

下面演示了如何编写一个具有两个类型变量 UV 的泛型函数:

function merge<U, V>(obj1: U, obj2: V) {
return {
...obj1,
...obj2
};
}

merge() 函数合并两个类型为 UV 的对象。它将两个对象的属性合并为一个对象。

类型推断将 merge() 函数的返回值推断为 UV 的交集类型,即 U & V

下面展示了如何使用 merge() 函数合并两个对象:

let result = merge(
{ name: 'John' },
{ jobTitle: 'Frontend Developer' }
);

console.log(result);

输出:

{ name: 'John', jobTitle: 'Frontend Developer' }

TypeScript 泛型的好处

以下是 TypeScript 泛型的好处:

  • 在编译时执行类型检查。
  • 消除类型转换。
  • 允许你实现通用算法。

概括

  • 使用 TypeScript 泛型开发可重用、通用且类型安全的函数、接口和类。