TypeScript学习之泛型(1)

143 阅读3分钟

前言

菜是原罪,被人吐槽,被人看不起。
唯有不断学习,提升自己,用牛逼堵上他们的嘴。

什么是泛型?可以用来做什么?

根据TypeScript的文档,泛型在C#和java这种强类型语言中,可以用泛型来创建重用的组件,这种组件能够支持多种类型的数据,这样就可以在避免书写重复的代码。
例如说,在没有使用泛型的时候:

function identify(arg: number): number {
    return arg;
}

第一次使用时,传参和返回都是number类型的,但是,下一次同样的代码,如果传参和返回都是string类型,意味着这个代码段需要再重新写一次,增加了不必要的代码。
有人可能就说了,可以使用any呀。

function identify(arg: any): any {
    return arg;
}

的确,any可以解决这个问题,达到复用的目的,但是,使用any类型意味着这个函数可以接受任何类型的arg参数,这样做的话,传参的类型和返回的类型,就可能不是一致的了,理论上来说,定义这样的函数,为的就是规范统一传参和返回值的类型吧。
那么,这个时候,我们就需要一种方法来是返回值的类型和传参的类型是相同的。那么,要怎么解决呢?
官方文档中提到了一个概念:类型变量,一种特殊的变量,只用于表示类型而不是值。
让我们使用这个类型变量,重新改造一下这个函数:

function identify<T>(arg: T): T {
    return arg;
}


let output = identify<number>(123);
let output2 = identify<string>("string");

类型变量T的作用,是帮助我们捕获用户传入的类型,之后我们就可以使用这个类型。 此时,这个identify函数,就是所谓的泛型了,因为它可以适用于多个类型,并且可以保证传参和返回值的类型是一致的。
上述泛型的调用,利用类型推论---即编译器会根据传入的参数自动地帮助我们确定类型变脸T的类型,所以也可以携程下面这种形式:

let output3 = identify("string");

当然,这种写法不适用于复杂场景。

使用泛型变量

使用泛型创建例子中identify这样的泛型函数,编译器要求在函数体必须正确的使用这个同的类型,也就是说,参数本身是不确定的,要当作是任意或者所有的类型,有点类似any了,只是any不能统一传参和返回值。

function identity<T>(arg: T): T {
    return arg;
}
------
function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}

上面这个例子的报错,是因为,使用了arg的.length属性,但是没有地方知名arg具有这个属性。因为类型变量代表着任意的类型,那么,arg就有可能是number类型的,而number类型是没有.length属性的。

那么,该如何解决这个问题呢?
从文档的例子看,传参不是单一数据类型,传参是元素类型是类型变量T的数组,返回值也是元素类型是类型变量T的数组。例如说,传入一个number数组,返回一个number数组,因为此时T的类型就是number,这里的意思就是把泛型变量T当作类型的一部分使用,而不是整个类型。

function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}
function loggingIdentity<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}