前端深入理解Typescript泛型概念
首先介绍一下泛性的概念
泛型程序设计(genericprogramming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。
泛型是指在定义函数,接口或者类的时候,不预先定义好具体的类型,而在使用的时候在指定类型的一种特性。
先举一个简单的例子
假设我们定义一个函数,它可以接收一个number类型做为参数,并且返回一个number类型。
functiongenericDemo(data:number):number{ returndata; }
按照以上的写法是没有问题的,但是如果我们要接受一个string并返回一个string呢?如果逻辑一样还要在写一遍吗?就像下面这样。
functiongenericDemo(data:string):string{ returndata; }
这显然代码是很冗余的,我们还有不使用any的写法吗?答案是显然易见的,可以使用范型的写法,就像下面这样。
functiongenericDemo(data:T):T{ returndata; }
我们在函数名称genericDemo后面声明了范型变量
多个类型参数
我们在定义范型的时候,也可以一次定义多个类型参数,像下面这样。
functionswap(tuple:[T,U]):[U,T]{ return[tuple[1],tuple[0]]; }
泛型接口
我们先定义一个范型接口Identities,然后定义一个函数identities()来使用这个范型接口
interfaceIdentities{ id1:T; id2:U; }
我在这里使用T和U作为我们的类型变量来演示任何字母(或有效的字母数字名称的组合)都是有效的类型—除了常规用途之外,您对它们的调用没有任何意义。
我们现在可以将这个接口应用为identity()的返回类型,修改我们的返回类型以符合它。我们还可以console.log这些参数和它们的类型,以便进一步说明:
functionidentities(arg1:T,arg2:U):Identities { console.log(arg1+":"+typeof(arg1)); console.log(arg2+":"+typeof(arg2)); letidentities:Identities ={ id1:arg1, id2:arg2 }; returnidentities; }
我们现在对identity()所做的是将类型T和U传递到函数和identity接口中,从而允许我们定义与参数类型相关的返回类型。
范型变量
使用泛型创建像identity这样的泛型函数时,编译器要求你在函数体必须正确的使用这个通用的类型。换句话说,你必须把这些参数当做是任意或所有类型。
我们先看下之前例子
functiongenericDemo(data:T):T{ returndata; }
如果我们想同时打印出data的长度。我们很可能会这样做
functiongenericDemo(data:T):T{ console.log(data.length);//Error:Tdoesn'thave.length returndata; }
如果这么做,编译器会报错说我们使用了data的.length属性,但是没有地方指明data具有这个属性。记住,这些类型变量代表的是任意类型,所以使用这个函数的人可能传入的是个数字,而数字是没有.length属性的。
现在假设我们想操作T类型的数组而不直接是T。由于我们操作的是数组,所以.length属性是应该存在的。我们可以像创建其它数组一样创建这个数组:
functiongenericDemo(data:Array ):Array { console.log(data.length); returndata; }
范型类
我们还可以在类属性和方法的意义上使类泛型。泛型类确保在整个类中一致地使用指定的数据类型。例如下面这种在ReactTypescript项目中的写法。
interfaceProps{ className?:string; ... } interfaceState{ submitted?:bool; ... } classMyComponentextendsReact.Component{ ... }
我们在这里使用与React组件一起使用的泛型,以确保组件的props和state是类型安全的。
泛型约束
我们先看一个常见的需求,我们要设计一个函数,这个函数接受两个参数,一个参数为对象,另一个参数为对象上的属性,我们通过这两个参数返回这个属性的值,比如:
functiongetValue(obj:object,key:string){ returnobj[key]//error }
我们会得到一段报错,这是新手TypeScript开发者常常犯的错误,编译器告诉我们,参数obj实际上是{},因此后面的key是无法在上面取到任何值的。
因为我们给参数obj定义的类型就是object,在默认情况下它只能是{},但是我们接受的对象是各种各样的,我们需要一个泛型来表示传入的对象类型,比如Textendsobject:
functiongetValue(obj:T,key:string){ returnobj[key]//error }
这依然解决不了问题,因为我们第二个参数key是不是存在于obj上是无法确定的,因此我们需要对这个key也进行约束,我们把它约束为只存在于obj属性的类型,这个时候需要借助到后面我们会进行学习的索引类型进行实现
functiongetValue(obj:T,key:U){ returnobj[key]//ok }
另外提一个多重泛型约束的写法,可以当作拓展:
interfacefirstInterface{ first():number } interfacesecondInterface{ second():string } classDemo{ ... }
在泛型里使用类类型
在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如:
functioncreate(type:{new():T;}):T{ returnnewtype(); }
参数type的类型{new():T}就表示此泛型T是可被构造的,在被实例化后的类型是泛型T。
总结
到此这篇关于前端深入理解Typescript泛型概念的文章就介绍到这了,更多相关Typescript泛型内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。