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() 函数适用于 any 类型的数组,包括数字,字符串和对象类型等等。

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);

这个例子显式地把 T 类型对象赋值为 number,传递给 getRandomElement() 函数。

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

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

在这个例子中,我们没有显式地给 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 泛型可以开发可复用的,具有通用性的和类型安全的函数,接口或者类。

教程来源于Github,感谢cody1991大佬的无私奉献,致敬!

技术教程推荐

SQL必知必会 -〔陈旸〕

Flutter核心技术与实战 -〔陈航〕

JavaScript核心原理解析 -〔周爱民〕

NLP实战高手课 -〔王然〕

Python自动化办公实战课 -〔尹会生〕

MySQL 必知必会 -〔朱晓峰〕

PyTorch深度学习实战 -〔方远〕

现代C++20实战高手课 -〔卢誉声〕

AI大模型企业应用实战 -〔蔡超〕