如何在 TypeScript 中使用泛型

介绍

在写动态和可重复使用的代码时,遵循不要重复原则(DRY)很重要。

使用通用代码,您可以编写动态和可重复使用的通用代码块;此外,您可以将通用代码应用于 TypeScript 中的类、接口和函数。

在本文中,您将将通用元素集成到您的TypeScript代码中,并将其应用到函数和类中,您还将学习如何使用界面在TypeScript中添加通用元素的限制。

前提条件

要成功完成本教程,您将需要以下内容:

  • 最新版本的 TypeScript 已安装在您的计算机上. 此 如何设置新的 TypeScript 项目教程可以帮助您实现这一点.
  • 最新版本的 ts-node 已安装. 如果您想测试和运行您的 TypeScript 代码,这项教程是必要的。

步骤1 - 了解通用药物

有时,您可能需要对不同数据类型重复相同的代码块. 以下是对两个不同的数据类型使用相同函数的示例:

1// for number type
2function fun(args: number): number {
3  return args;
4}
5
6// for string type
7function fun(args: string): string {
8  return args;
9}

请注意,在本示例中,对于数字字符串类型重复相同的函数。

有一个名为任何的类型,您可以使用它来实现代码中的通用药物的相同效果。使用任何类型将允许您退出类型检查。

要在实践中看到这一点,请将任何类型应用到前面的代码示例中:

1function fun(args: any): any {
2 return args;
3}

数字字符串类型替换为任何类型,使函数成为通用函数,但使用任何类型意味着乐趣函数可以接受任何数据,因此,您也会失去类型安全性。

虽然使用任何类型是使您的 TypeScript 代码更通用的一种方法,但它可能并不总是最好的选择。

步骤2 - 创建安全型通用药物

要创建类型安全的通用元素,您需要使用Type参数。Type参数被定义为TT>

返回乐趣函数,使用T使您的通用函数安全:

1[label index.ts]
2function fun<T>(args:T):T {
3  return args;
4}

因此,fun现在是一个类型安全的通用函数. 要测试这种类型安全的通用函数,请创建一个名为result的变量,并用一个string类型的参数将其设置为fun

1[label index.ts]
2let result = fun<string>("Hello World");

尝试使用乐趣函数与数字类型. 将参数设置为200:

1[label index.ts]
2let result2 = fun<number>(200);

如果您想看到此代码的结果,您可以添加console.log语句,将resultresult2打印到控制台:

1[label index.ts]
2console.log(result);
3console.log(result2);

最后,你的代码应该是这样的:

 1[label index.ts]
 2function fun<T>(args:T):T {
 3  return args;
 4}
 5
 6let result = fun<string>("Hello World");
 7let result2 = fun<number>(200);
 8
 9console.log(result);
10console.log(result2);

使用ts-node在控制台中运行此 TypeScript 代码:

1npx ts-node index.ts

代码不会出现错误,你会看到这个输出:

1[secondary_label Output]
2Hello World
3200

现在您可以为具有单个参数的函数创建安全型号的通用元素,并且了解如何为具有多个参数的多种函数创建通用元素也很重要。

步骤 3 – 使用具有许多类型的参数的通用药物

如果函数中有许多参数,您可以使用不同的字母来表示类型,您不需要只使用T:

1[label params.ts]
2function fun<T, U, V>(args1:T, args2: U, args3: V): V {
3  return args3;
4}

此函数采用3个参数,即args1,args2arg3,并返回args3。这些参数不限于特定类型,因为T,UV被用作fun函数参数的通用类型。

创建一个名为result3的变量,并将其分配到fun中。 包括< string, number, boolean>类型来填写T,UV通用类型。

1[label params.ts]
2let result3 = fun<string, number, boolean>('hey', 3, false);

这将返回第三个参数,即false。 若要查看此,您可以使用console.log语句:

1[label params.ts]
2console.log(result3);

运行ts-node命令以查看您的console.log声明输出:

1npx ts-node params.ts

这将是产量:

1[secondary_label Output]
2false

现在您可以为具有多个参数的函数创建通用类型,就像函数一样,通用类型也可以与接口一起使用。

步骤 4 – 创建通用类

类可以是通用函数,类也可以是通用函数。在角度(<>)中使用参数,就像在函数中一样。

创建一个使用数字字符串输入的类,并使用这些输入创建一个数组。

1[label classes.ts]
2class customArray<T> {
3  private arr: T[] = [];
4}

现在,你的数组包含不同类型的项目已经到位. 创建一个名为getItems的方法,返回customArray数组:

1[label classes.ts]
2getItems (arr: T[]) {
3  return this.arr = arr;
4}

创建名为addItem的方法,将新项目添加到customArray数组的末尾:

1[label classes.ts]
2addItem(item:T) {
3  this.arr.push(item);
4}

arr:T[]参数意味着数组中的项目可以是任何类型的,因此customArray可以是数组,布尔符号或字符串。

添加一种名为removeItem的方法,从customArray中删除指定的项目:

1[label classes.ts]
2removeItem(item: T) {
3  let index = this.arr.indexOf(item);
4    if(index > -1)
5      this.arr.splice(index, 1);
6}

addItem方法一样,removeItem采取任何类型的参数,并从customArray数组中删除指定的参数。

现在,通用类customArray已完成,为数字字符串类型创建一个customArray实例。

声明名为numObj的变量为number类型的customArray实例:

1[label classes.ts]
2let numObj = new customArray<number>();

使用addItem方法将数字10添加到numObj:

1[label classes.ts]
2numObj.addItem(10);

由于customArray是通用的,所以它也可以用来创建一个字符串的数组。创建一个名为strObj的变量,为字符串类型创建一个等于customArray的实例:

1[label classes.ts]
2let strObj = new customArray<string>();

使用addItem方法将字符串Robin添加到strObj数组中。

1[label classes.ts]
2strObj.addItem(Robin);

若要查看代码的结果,请为numObjstrObj创建一个console.log声明:

1[label classes.ts]
2console.log(numObj);
3console.log(strObj);

最后,你的代码应该是这样的:

 1[label classes.ts]
 2class customArray<T> {
 3  private arr: T[] = [];
 4
 5  getItems(arr: T[]) {
 6    return this.arr = arr;
 7  }
 8
 9  addItem(item:T) {
10    this.arr.push(item);
11  }
12
13  removeItem(item: T) {
14    let index = this.arr.indexOf(item);
15      if(index > -1)
16        this.arr.splice(index, 1);
17  }
18}
19
20let numObj = new customArray<number>();
21numObj.addItem(10);
22
23let strObj = new customArray<string>();
24strObj.addItem(Robin);
25
26console.log(numObj);
27console.log(strObj);

运行ts-node后,这就是你将收到的输出:

1[secondary_label Output]
2customArray { arr: [ 10 ] }
3customArray { arr: [ 'Robin' ] }

您使用了customArray类用于数字字符串类型,您可以通过使用通用类型来实现这一目标,但是,使用通用药物确实存在一些限制。

步骤5 – 了解通用限制

到目前为止,您已经使用通用元素创建了函数和类,但使用通用元素有一个缺点. 要在操作中看到此缺点,请写一个名为getLength的函数,该函数的参数返回长度:

1[label constraints.ts]
2function getLength<T>(args: T) : number {
3  return args.length;
4}

只要通过类型具有长度属性,此函数将工作,但没有长度属性的数据类型将产生例外。

要做到这一点,您首先需要创建一个名为funcArgs的界面,并定义一个长度属性:

1[label constraints.ts]
2interface funcArgs {
3  length: number;
4}

现在,更改getLength函数并扩展它以包括funcArgs界面作为限制:

1[label constraints.ts]
2function getLength<T extends funcArgs>(args:T) : number {
3  return args.length;
4}

您已使用接口创建了一种通用限制。 此外,您还与此接口扩展了getLength函数。 它现在需要长度作为所需参数。

要在行动中看到这一点,请声明一个名为result4的变量,并将其分配到getLength中,其参数为3:

1[label constraints.ts]
2let result4 = getLength(3);

这会返回错误,因为不包含长度参数的值:

1[secondary_label Output]
2⨯ Unable to compile TypeScript:
3index.ts:53:25 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'funcArgs'.
4
553 let result4 = getLength(3);

要调用getLength函数,您需要包含一个长度参数以及一个名称参数:

1[label constraints.ts]
2let result = getLength({ length: 5, name: 'Hello'});

此通话具有长度属性,您的函数将正常工作,不会显示任何错误消息。

结论

在本教程中,您成功地将通用元素整合到您的TypeScript函数和类中,您还包括了对通用元素的限制。

如果您想了解如何在 VS Code 中使用 TypeScript,则本文 如何在 Visual Studio Code 中使用 TypeScript是开始的好地方。

Published At
Categories with 技术
Tagged with
comments powered by Disqus