介绍
在编写动态和可重用的代码时,遵循不要重复自己 ( DRY ) 原则很重要。使用泛型可以帮助您在 TypeScript 代码中实现这一点。
使用泛型,您可以编写动态且可重用的泛型代码块。此外,您可以将 TypeScript 中的泛型应用于类、接口和函数。
在本文中,您将把泛型集成到您的 TypeScript 代码中,并将它们应用到函数和类中。您还将学习如何使用接口向 TypeScript 中的泛型添加约束。
先决条件
要成功完成本教程,您需要具备以下条件:
- 您的机器上安装了最新版本的 TypeScript。此如何设置新的 TypeScript 项目教程可以帮助您完成此操作。
ts-node
安装的最新版本。如果你想测试和运行你的 TypeScript 代码,这是必要的。这个使用 ts-node教程轻松运行 TypeScript 脚本是一个很好的起点。- 熟悉在 TypeScript 中编写函数和类。
步骤 1 — 理解泛型
有时,您可能希望为不同的数据类型重复相同的代码块。这是用于两种不同数据类型的相同函数的示例:
// for number type
function fun(args: number): number {
return args;
}
// for string type
function fun(args: string): string {
return args;
}
请注意,在此示例中,对number
和string
类型重复了相同的功能。泛型可以帮助您编写通用方法,而不是编写和重复相同的代码块,如上例所示。
有一种称为 的类型any
,您可以使用它来实现与代码中的泛型相同的效果。使用该any
类型将允许您选择退出类型检查。然而,any
不是type-safe
。这意味着 usingany
可以给你一个例外。
要在实践中看到这一点,请将any
类型应用于前面的代码示例:
function fun(args: any): any {
return args;
}
交换number
和string
类型到any
类型使函数通用。但是有一个问题——使用any
类型意味着fun
函数可以接受任何数据。结果,您也失去了类型安全性。
尽管使用any
类型是一种使您的 TypeScript 代码更通用的方法,但它可能并不总是最佳选择。在下一步中,您将探索用于创建类型安全泛型的不同选项。
步骤 2 — 创建类型安全泛型
要创建类型安全的泛型,您需要使用Type
参数。Type
参数由T
或定义<T>
。它们表示传递给类、接口和函数的参数的数据类型。
回到fun
函数,使用T
使您的泛型函数类型安全:
function fun<T>(args:T):T {
return args;
}
因此,fun
现在是一个类型安全的泛型函数。要测试此类型安全的泛型函数,请创建一个名为的变量result
,fun
并使用string
类型参数将其设置为等于。参数将是Hello World
字符串:
let result = fun<string>("Hello World");
尝试将fun
函数与number
类型一起使用。设置参数等于200
:
let result2 = fun<number>(200);
如果您想查看此代码的结果,可以包含console.log
要打印result
和result2
控制台的语句:
console.log(result);
console.log(result2);
最后,您的代码应如下所示:
function fun<T>(args:T):T {
return args;
}
let result = fun<string>("Hello World");
let result2 = fun<number>(200);
console.log(result);
console.log(result2);
用于ts-node
在控制台中运行此 TypeScript 代码:
- npx ts-node index.ts
代码呈现没有错误。你会看到这个输出:
OutputHello World
200
您现在可以为具有一个参数的函数创建类型安全的泛型。了解如何为具有多种不同类型的多个参数的函数创建泛型也很重要。
第 3 步 – 使用具有多种类型参数的泛型
如果一个函数中有很多参数,可以用不同的字母来表示类型。您不必只使用T
:
function fun<T, U, V>(args1:T, args2: U, args3: V): V {
return args3;
}
这个函数有3个参数,args1
,args2
,和arg3
,并返回args3
。这些参数不限于特定类型。这是因为T
、U
和V
用作fun
函数参数的泛型类型。
创建一个名为的变量result3
并将其分配给fun
. 包括<string, number, boolean>
类型填写T
,U
和V
泛型类型。对于参数,包括括号内的字符串、数字和布尔值:
let result3 = fun<string, number, boolean>('hey', 3, false);
这将返回第三个参数,false
。要查看这一点,您可以使用以下console.log
语句:
console.log(result3);
运行ts-node
命令以查看您的console.log
语句输出:
- npx ts-node params.ts
这将是输出:
Outputfalse
现在您可以为具有多个参数的函数创建泛型类型。与函数一样,泛型也可以与classes
和一起使用interfaces
。
第 4 步 – 创建泛型类
像泛型函数一样,类也可以是泛型的。与函数一样,使用尖括号type
( <>
) 中的参数。然后在<T>
整个类中使用该类型来定义方法和属性。
创建一个接受number
和string
输入的类,并使用这些输入创建一个数组。使用<T>
作为泛型类型参数:
class customArray<T> {
private arr: T[] = [];
}
现在,接收不同类型项目的数组就位。创建一个getItems
返回customArray
数组的方法:
getItems (arr: T[]) {
return this.arr = arr;
}
创建一个名为的方法addItem
,用于将新项目添加到customArray
数组的末尾:
addItem(item:T) {
this.arr.push(item);
}
该arr: T[]
参数是指该阵列中的项目可以是任何类型的。因此customArray
可以是数字、布尔值或字符串的数组。
添加一个称为removeItem
从 中删除指定项目的方法customArray
:
removeItem(item: T) {
let index = this.arr.indexOf(item);
if(index > -1)
this.arr.splice(index, 1);
}
与addItem
方法一样,removeItem
接受任何类型的参数并从customArray
数组中删除指定的参数。
现在泛型类customArray
已经完成。创建customArray
fornumber
和string
类型的实例。
声明一个名为numObj
set的变量等于customArray
fornumber
类型的实例:
let numObj = new customArray<number>();
使用该addItem
方法将数字添加10
到numObj
:
numObj.addItem(10);
由于customArray
是通用的,它也可用于创建字符串数组。创建一个名为strObj
set的变量,它等于customArray
for 字符串类型的实例:
let strObj = new customArray<string>();
使用addItem
方法将字符串添加Robin
到strObj
数组中。
strObj.addItem(“Robin”);
要查看代码的结果,建立一个console.log
两个语句numObj
和strObj
:
console.log(numObj);
console.log(strObj);
最后,您的代码应如下所示:
class customArray<T> {
private arr: T[] = [];
getItems(arr: T[]) {
return this.arr = arr;
}
addItem(item:T) {
this.arr.push(item);
}
removeItem(item: T) {
let index = this.arr.indexOf(item);
if(index > -1)
this.arr.splice(index, 1);
}
}
let numObj = new customArray<number>();
numObj.addItem(10);
let strObj = new customArray<string>();
strObj.addItem(“Robin”);
console.log(numObj);
console.log(strObj);
运行后ts-node
,这是您将收到的输出:
OutputcustomArray { arr: [ 10 ] }
customArray { arr: [ 'Robin' ] }
您将customArray
类用于number
和string
类型。您可以通过使用泛型类型来实现这一点。但是,使用泛型确实有一些限制。这将在下一步中讨论。
步骤 5 — 了解通用约束
到目前为止,您已经使用泛型创建了函数和类。但是使用泛型有一个缺点。要查看此缺点的实际效果,请编写一个调用的函数,该函数getLength
将返回length
函数参数的
function getLength<T>(args: T) : number {
return args.length;
}
只要传递的类型有length
属性,这个函数就会工作,但是没有length
属性的数据类型会抛出异常。
这个问题有一个解决方案——创建通用约束。为此,您首先需要创建一个名为的接口funcArgs
并定义一个length
属性:
interface funcArgs {
length: number;
}
现在,更改getLength
函数及其extend
包含的funcArgs
接口作为约束:
function getLength<T extends funcArgs>(args:T) : number {
return args.length;
}
您已经使用接口创建了通用约束。此外,您还getLength
使用此接口扩展了功能。它现在需要length
作为必需参数。getLength
使用没有长度参数的参数访问此函数将显示异常消息。
要查看此操作,请声明一个名为的变量result4
并将其分配给getLength
with3
作为其参数:
let result4 = getLength(3);
这将返回错误,因为length
不包括参数值:
Output⨯ Unable to compile TypeScript:
index.ts:53:25 - error TS2345: Argument of type 'number' is not assignable to parameter of type 'funcArgs'.
53 let result4 = getLength(3);
要调用该getLength
函数,您需要包含一个length
参数和一个name
参数:
let result = getLength({ length: 5, name: 'Hello'});
这是调用我们函数的正确方式。这个调用有一个length
属性,你的函数会很好地工作。它不会显示任何错误消息。
结论
在本教程中,您成功地将泛型集成到您的 TypeScript 函数和类中。您还包括对泛型的约束。
下一步,您可能有兴趣学习如何将 TypeScript 与 React 结合使用。如果你想学习如何在 VS Code 中使用 TypeScript,这篇如何在 Visual Studio Code 中使用 TypeScript文章是一个很好的起点。