TypeScript使用泛型编程 – TypeScript开发教程

上一章TypeScript教程请查看:TypeScript名称空间和模块的区别
TypeScript泛型是一个提供创建可重用组件方法的工具,它创建了一个可以处理多种数据类型而不是单一数据类型的组件。它允许用户使用这些组件并使用自己的类型,泛型确保了程序的灵活性以及长期的可伸缩性。
泛型在不影响性能或生产率的情况下提供类型安全性,TypeScript使用泛型,类型变量表示类型。泛型函数的类型与非泛型函数类似,首先列出类型参数,类似于函数声明。
在泛型中,我们需要在开(< )和闭(> )括号之间编写类型参数,这使它成为强类型集合。泛型使用一种特殊类型的类型变量< T> ,它表示类型。泛型集合只包含类似类型的对象。
【TypeScript使用泛型编程 – TypeScript开发教程】在TypeScript中,我们可以创建泛型类、泛型函数、泛型方法和泛型接口。TypeScript泛型与c#和Java泛型非常相似。
例子
下面的示例帮助我们清楚地理解泛型。

function identity< T>(arg: T): T { return arg; } let output1 = identity< string>("myString"); let output2 = identity< number>( 100 ); console.log(output1); console.log(output2);

当我们编译上面的文件时,它会返回相应的JavaScript文件,如下所示。
function identity(arg) { return arg; } var output1 = identity("myString"); var output2 = identity(100); console.log(output1); console.log(output2);

泛型的优点泛型主要有三个优点。它们如下:
  • 类型安全: 我们只能在泛型中保存单一类型的对象。它不允许存储其他对象。
  • 不需要类型转换: 不需要对对象进行类型转换。
  • 编译时检查: 在编译时检查,所以问题不会在运行时发生。
为什么需要泛型?通过下面的示例,我们可以理解对泛型的需求。
function getItems(items: any[] ) : any[] { return new Array().concat(items); } let myNumArr = getItems([10, 20, 30]); let myStrArr = getItems(["Hello", "srcmini"]); myNumArr.push(40); // ok myNumArr.push("Hello TypeScript"); // ok myStrArr.push("Hello SSSIT"); // ok myStrArr.push(40); // ok console.log(myNumArr); // [10, 20, 30, 40, "Hello TypeScript"] console.log(myStrArr); // ["Hello", "srcmini", "Hello SSSIT", 40]

在上面的例子中,getItems()函数接受任意类型的数组。函数的作用是: 创建一个any类型的新数组,将项连接到其中并返回这个新数组。由于我们使用了任何数据类型,所以可以将任何类型的项传递给函数。但是,这可能不是添加项目的正确方式。我们必须向number数组添加数字,向string数组添加字符串,但是我们不想向string数组添加数字,反之亦然。
为了解决这个问题,TypeScript引入了泛型。在泛型中,类型变量只接受用户在声明时提供的特定类型,它还保存类型检查信息。
因此,我们可以把上面的函数写成下面的泛型函数。
function getItems< T>(items : T[] ) : T[] { return new Array< T>().concat(items); } let arrNumber = getItems< number>([10, 20, 30]); let arrString = getItems< string>(["Hello", "srcmini"]); arrNumber.push(40); // ok arrNumber.push("Hi! srcmini"); // 编译错误 arrString.push("Hello TypeScript"); // ok arrString.push(50); // 编译错误 console.log(arrNumber); console.log(arrString);

在上面的例子中,类型变量T指定了尖括号getItems< T>中的函数。此变量还指定参数的类型和返回值,它确保在函数调用时指定的数据类型也是参数和返回值的数据类型。
通用函数getItems()接受数字数组和字符串数组。当我们调用函数getItems< number>([10,20,30])时,它将用数字替换T。因此,参数的类型和返回值将是number数组。类似地,对于函数getItems< string>([“ Hello” , “ srcmini” ]),参数类型和返回值都是string数组。现在,如果我们尝试在arrNumber中添加一个字符串或在arrString数组中添加一个数字,编译器会显示一个错误。因此,它保留了类型检查的优点。
在TypeScript中,我们也可以在不指定类型变量的情况下调用泛型函数。TypeScript编译器将根据参数值的数据类型设置函数的T值。
多类型的变量在TypeScript泛型中,我们可以定义不同名称的多类型变量。我们可以通过下面的例子来理解它。
例子
function displayDataType< T, U>(id:T, name:U): void { console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name)); } displayDataType< number, string>(101, "Van");

泛型与非泛型类型我们还可以将泛型类型与其他非泛型类型一起使用。
例子
function displayDataType< T>(id:T, name:string): void { console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name)); } displayDataType< number>(1, "Van");

泛型类TypeScript还支持泛型类,泛型类型参数在类名后面的尖括号(< >)中指定,泛型类可以具有泛型字段或方法。
例子
class StudentInfo< T,U> { private Id: T; private Name: U; setValue(id: T, name: U): void { this.Id = id; this.Name = name; } display():void { console.log(`Id = ${this.Id}, Name = ${this.Name}`); } } let st = new StudentInfo< number, string>(); st.setValue(101, "AAA"); st.display(); let std = new StudentInfo< string, string>(); std.setValue("201", "BBB"); std.display();

泛型接口泛型类型也可以与接口一起使用。通过下面的示例,我们可以理解通用接口。
例子
interface People { name: string age: number } interface Celebrity extends People { profession: string } function printName< T extends Celebrity>(theInput: T): void { console.log(`Name: ${theInput.name} \nAge: ${theInput.age} \nProfession: ${theInput.profession}`); } let player: Celebrity = { name: 'La Oreja', age: 30, profession: 'VVV' } printName(player);

泛型接口作为函数类型我们还可以使用泛型接口作为函数类型,下面的示例可以理解它。
例子
interface StudentInfo< T, U> { (id: T, value: U): void; }; function studentData(id: number, value:string):void { console.log('Id = '+ id + ', \nName = ' + value) } let std: StudentInfo< number, string> = studentData; std(11, "AAA");

泛型约束众所周知,TypeScript泛型类型允许处理任何和所有数据类型。但是,我们可以使用约束将其限制为某些类型。在下面的示例中,我们将创建一个具有单个.length属性的接口。我们将使用这个接口和“extends”关键字来表示我们的约束。
例子
nterface Lengthwise { length: number; } function loggingIdentity< T extends Lengthwise>(arg: T): T { console.log("Length: " +arg.length); // 它有一个.length属性,所以没有发现更多的错误 return arg; } loggingIdentity({length: 10, value: 9}); loggingIdentity(3); // 编译错误, number doesn't have a .length property

类的泛型约束下面给出了构造函数与类类型的实例端之间的泛型约束关系的更高级示例。
例子
class Student { Id: number; Name: string; constructor(id:number,name:string) { this.Id = id; this.Name = name; } } function display< T extends Student>(per: T): void { console.log(`${ st.Id} ${st.Name}` ); } var st = new Student(101, "\nAAVBV"); display(st);

    推荐阅读