本文已参与「新人创作礼」活动,一起开启掘金创作之路。
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;