我们来模拟一个场景:某个服务提供了一些不同类型的数据,我们需要先通过一个中间件对这些数据进行一个基本的处理(比如验证,容错等),再对其进行使用。那么用JavaScript来写应该是这样的
//模拟服务,提供不同的数据。这里模拟了一个字符串和一个数值varservice={getStringValue:function(){return"astringvalue";},getNumberValue:function(){return20;}};//处理数据的中间件。这里用log来模拟处理,直接返回数据当作处理后的数据functionmiddleware(value){console.log(value);returnvalue;}//JS中对于类型并不关心,所以这里没什么问题varsValue=middleware(service.getStringValue());varnValue=middleware(service.getNumberValue());改写成TypeScript先来看看对服务的改写,TypeScript版的服务有返回类型:
constservice={getStringValue():string{return"astringvalue";},getNumberValue():number{return20;}};为了保证在对sValue和nValue的后续操作中类型检查有效,它们也会有类型(如果middleware类型定义得当,可以推导,这里我们先显示定义其类型)
constsValue:string=middleware(service.getStringValue());constnValue:number=middleware(service.getNumberValue());现在的问题是middleware要怎么样定义才既可能返回string,又可能返回number,而且还能被类型检查正确推导出来?
functionmiddleware(value:any):any{console.log(value);returnvalue;}是的,这个办法可以检查通过。但它的问题在于middleware内部失去了类型检查,在后在对sValue和nValue赋值的时候,也只是当作类型没有问题。简单的说,是有“假装”没问题。
functionmiddleware1(value:string):string{...}functionmiddleware2(value:number):number{...}当然也可以用TypeScript的重载(overload)来实现
functionmiddleware(value:string):string;functionmiddleware(value:number):number;functionmiddleware(value:any):any{//实现一样没有严格的类型检查}这种方法最主要的一个问题是……如果我有10种类型的数据,就需要定义10个函数(或重载),那20个,200个呢……
现在我们切入正题,用泛型来解决这个问题。那么这就需要解释一下什么是泛型了:泛型就是指定一个表示类型的变量,用它来代替某个实际的类型用于编程,而后通过实际调用时传入或推导的类型来对其进行替换,以达到一段使用泛型程序可以实际适应不同类型的目的。
虽然这个解释已经很接地气了,但是理解起来还是不如一个实例来得容易。我们来看看middleware的泛型实现是怎么样的
我们直接从VSCode的提示可以看出来,对于middleware
我们也可以在调用的时候,小括号前显示指定T代替的类型,比如mdiddleware
相信大家都已经对数组有所了解,比如string[]表示字符串数组类型。其实在早期的TypeScript版本中没有这种数组类型表示,而是采用实例化的泛型Array
除此之外,TypeScript中还有一个很常用的泛型类,Promise
所以,泛型类其实多数时候是应用于容器类。假设我们需要实现一个FilteredList,我们可以向其中add()(添加)任意数据,但是它在添加的时候会自动过滤掉不符合条件的一些,最终通过getall()输出所有符合条件的数据(数组)。而过滤条件在构造对象的时候,以函数或Lambda表达式提供。
typePredicate
比如,我们有IAnimal这样一个接口,然后写一个run工具函数,它可以让动物跑起来,而且它会返回这个动物实例本身(以便链式调用)。先来定义类型
functionrun
正解是使用泛型约束,将TAnimal约束为实现了IAnimal。这需要在定义类型变量的使用使用extends来约束:
functionrun
有时候我们希望传入某个工具方法的参数是一个类型,这样就可以通过new来生成对象。这在TypeScript中通常是使用构造函数来约束的,比如
functioncreate
在使用泛型的时候,当然不会限制只使用一个类型变量,我们可以使用多个,比如可以这样定义一个Pair类
classPair