📜  TypeScript泛型

📅  最后修改于: 2021-01-11 12:51:32             🧑  作者: Mango

TypeScript泛型

TypeScript Generics是一种工具,提供了一种创建可重用组件的方法。它创建了一个可以使用多种数据类型而不是单个数据类型的组件。它允许用户使用这些组件并使用自己的类型。泛型可确保该程序具有长期的灵活性和可扩展性。

泛型在不影响性能或生产率的情况下提供了类型安全性。 TypeScript使用泛型和类型变量来表示类型。泛型函数的类型就像非泛型函数一样,首先列出类型参数,这与函数声明类似。

在泛型中,我们需要在开括号(<)闭括号(>)之间编写类型参数,以使其成为强类型集合。泛型使用一种特殊的类型变量来表示类型。泛型集合仅包含相似类型的对象。

在TypeScript中,我们可以创建通用类,通用函数,通用方法和通用接口。 TypeScript泛型几乎类似于C#和Java泛型。

下面的示例帮助我们清楚地了解泛型。

function identity(arg: T): T {  
    return arg;  
}  
let output1 = identity("myString");  
let output2 = identity( 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", "JavaTpoint"]);
myNumArr.push(40); // Correct
myNumArr.push("Hello TypeScript"); // Correct
myStrArr.push("Hello SSSIT"); // Correct
myStrArr.push(40); // Correct
console.log(myNumArr); // [10, 20, 30, 40, "Hello TypeScript"]
console.log(myStrArr); // ["Hello", "JavaTpoint", "Hello SSSIT", 40]

输出:

在上面的示例中, getItems()函数接受类型为any的数组。 getItems()函数创建类型为any的新数组,将项目连接到该数组并返回此新数组。由于我们使用了任何数据类型,因此可以将任何类型的项目传递给函数。但是,这可能不是添加项目的正确方法。我们必须号码数字数组字符串添加到字符串数组,但我们不希望号添加到字符串数组或反之亦然。

为了解决这个问题,TypeScript引入了泛型。在泛型中,类型变量仅接受用户在声明时提供的特定类型。它还保留类型检查信息。

因此,我们可以将以下函数编写为通用函数,如下所示。

function getItems(items : T[] ) : T[] {
    return new Array().concat(items);
}
let arrNumber = getItems([10, 20, 30]);
let arrString = getItems(["Hello", "JavaTpoint"]);
arrNumber.push(40); // Correct
arrNumber.push("Hi! Javatpoint"); // Compilation Error
arrString.push("Hello TypeScript"); // Correct
arrString.push(50); // Compilation Error
console.log(arrNumber);
console.log(arrString);

输出:

在上面的示例中,类型变量T在尖括号getItems 指定函数。此变量还指定参数的类型和返回值。它确保在函数调用时指定的数据类型也将是参数和返回值的数据类型。

通用函数getItems()接受numbers数组和字符串数组。当我们调用函数getItems ([10,20,30])时,它将用数字替换T。因此,参数的类型和返回值将是数字数组。同样,对于函数getItems < 字符串>([[“ Hello”,“ JavaTpoint”])) ,参数类型和返回值将为字符串数组。现在,如果尝试在arrNumber中添加字符串或在arrString数组中添加数字,则编译器将显示错误。因此,它保留了类型检查的优势。

在TypeScript中,我们也可以不指定类型变量而调用泛型函数。 TypeScript编译器将根据参数值的数据类型在函数上设置T的值。

多类型变量

在TypeScript泛型中,我们可以使用其他名称定义多类型变量。我们可以通过以下示例了解它。

function displayDataType(id:T, name:U): void { 
  console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name));  
}
displayDataType(101, "Abhishek");

输出:

泛型与非泛型类型

我们还可以将泛型类型与其他非泛型类型一起使用。

function displayDataType(id:T, name:string): void { 
  console.log("DataType of Id: "+typeof(id) + "\nDataType of Name: "+ typeof(name));  
}
displayDataType(1, "Abhishek");

输出:

泛型类

TypeScript还支持通用类。泛型类型参数在类名称后的尖括号(<>)中指定。通用类可以具有通用字段或方法。

class StudentInfo
{ 
    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();
st.setValue(101, "Virat");
st.display();
let std = new StudentInfo();
std.setValue("201", "Rohit");
std.display();

输出:

泛型接口

泛型类型也可以与接口一起使用。我们可以通过以下示例了解通用接口。

interface People {
    name: string
    age: number
}
interface Celebrity extends People {
    profession: string
}
function printName(theInput: T): void {
    console.log(`Name: ${theInput.name} \nAge: ${theInput.age} \nProfession: ${theInput.profession}`);
}
let player: Celebrity = {
    name: 'Rohit Sharma', age: 30, profession: 'Cricket Player'
}
printName(player);

输出:

泛型接口作为函数类型

我们还可以将泛型接口用作函数类型。下面的例子可以理解它。

interface StudentInfo
{
    (id: T, value: U): void;
};
function studentData(id: number, value:string):void { 
    console.log('Id = '+ id + ', \nName = ' + value)
}
let std: StudentInfo = studentData;
std(11, "Rohit Sharma");

通用约束

众所周知,TypeScript泛型类型允许使用任何和所有数据类型。但是,我们可以通过使用约束将其限制为某些类型。在下面的示例中,我们将创建一个具有单个.length属性的接口。我们将使用此接口,并且使用“ extended ”关键字来表示我们的约束。

interface Lengthwise {
    length: number;
}
function loggingIdentity(arg: T): T {
    console.log("Length: " +arg.length);  // It has a .length property, so no more error found
    return arg;
}
loggingIdentity({length: 10, value: 9});
loggingIdentity(3);  // Compilation Error, number doesn't have a .length property

输出:

Length: 10
Length: undefined

类的一般约束

下面给出了构造函数和类类型的实例端之间通用约束关系的更高级示例。

class Student {
    Id: number;
    Name: string;

    constructor(id:number,  name:string) { 
        this.Id = id;
        this.Name = name;
    }
}
function display(per: T): void {
    console.log(`${ st.Id} ${st.Name}` );
}
var st = new Student(101, "\nVirat Kohli");
display(st);

输出: