从0开始学习TypeScript-泛型

128 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

1.其他语言的泛型

今天是我学习Typescript的第3天,今天学习TypeScript泛型。

像C#和Java这语言中,可以使用泛型来创建可重用的类型,一个类型可以支持多种类型的数据,这样用户就可以以自己的数据类型来使用这个类型了,专业的说法总是看的别扭,下面用C#代码举例总结前面这句话,Hello 接受一个T类型的,在实例化时可以任意类型,一目了然。

public class Hello<T>
{
    public T retMethod()
    {
        T t = default(T);
        return t;
    }
}
//给T类型赋值string
Hello<string> hello = new Hello<string>();
//给T类型赋值int
Hello<int> hello = new Hello<int>();
hello.retMethod();
2.Typescript中的泛型

我们创建一个使用泛型的例子,HelloWorld函数,这个函数会返回任何传入它的值,不用泛型的话,这个函数是下面这样:

//入参number类型,返回值number
function HelloWorld(arg: number): number {
    return arg;
}

或者使用any类型来定义函数,

//使用Any类型
function HelloWorld(arg: any): any {
    return arg;
}

但是any类型会导致这个函数可以接收任何类型的arg参数,这样就丢失一些信息,传入的类型与返回的类型应该是相同的,如果我们传入一个数字,我们只知道任何类型的值都有可能被返回,我们需要一种方法使返回值的类型与传入参数的类型是相同的,我们也加入T类型,我们把这个HelloWorld函数叫做泛型,跟开始使用c# 的例子中一样,它可以适用于多个类型,无论是string还是number

//加入泛型T
function HelloWorld<T>(arg: T): T {
    return arg;
}

我们定义泛型函数后,可以用两种方法使用, 第一种是,传入所有的参数,包含类型参数,我们指定T是string类型,并使用<>括起来:

let output = HelloWorld<string>("myString"); 

第二种方法利用类型推论,编译器会根据传入的参数自动地帮助我们确定T的类型,感觉很智能有没有,其实C#中也支持,如果又复杂类型建议还是带上,防止有些隐藏bug绕过了编译器,但是确实在运行时是不正确的代码

let output = HelloWorld("myString"); 

//c#中也支持类型推断
public T retMethod<T>(T t)
{
    return t;
}
 hello.retMethod("泛型string类型参数");
3.使用泛型变量

使用泛型创建HelloWorld这样的泛型函数时,编译器要求在函数体必须正确的使用这个通用的类型。 必须把这些参数当做是任意或所有类型。

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

打印出arg的长度

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

如果这么做,编译器会报错说我们使用了arg的.length属性,但是没有地方指明arg具有这个属性。 记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有 .length属性的。

现在我们想操作T类型的数组而不直接是T,由于我们操作的是数组,所以.length属性是存在的,我们可以像创建其它数组一样创建这个数组:

function loggingHelloWorld<T>(arg: T[]): T[] {
    console.log(arg.length);  
    return arg;
}

我们也可以使用Array:

function loggingHelloWorld<T>(arg: Array<T>): Array<T> {
    console.log(arg.length);  
    return arg;
}
4.泛型类型

上面我们创建了HelloWorld通用函数,可以适用于不同的类型,我们看一下函数本身的类型

function HelloWorld<T>(arg: T): T {
    return arg;
}
let helloWorld: <T>(arg: T) => T = HelloWorld;

我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以。

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

let helloWorld: <U>(arg: U) => U = HelloWorld;

带有调用签名的对象字面量来定义泛型函数:

function HelloWorld<T>(arg: T): T {
    return arg;
}
let helloWorld: {<T>(arg: T): T} = HelloWorld;

触类旁通,写一个泛型接口

interface GenericIdentityFn {
    <T>(arg: T): T;
}

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

let helloWorld: GenericIdentityFn = HelloWorld;