当前位置: 首页 > 科技观察

关于Typescript的一些又爱又恨的事情——TypeGuard,Narrowing

时间:2023-03-13 11:58:50 科技观察

本文已获得许可发布。由于JavaScript本身是一门弱语言,在开发中经常会因为不知道变量的类型而烦恼。即使变量的定位通过命名更明确了一点,我们还是很难一眼就知道它的类型。即使当这个变量是一个对象时,我们也更难知道它里面有哪些键,所以人们逐渐开始使用TypeScript作为主要的开发工具。不知道大家在使用TypeScript进行开发的时候,是否觉得TypeScript特别烦人。虽然了解这些类型的检查是非常好的,但它可以帮助我们减少许多可能发生的潜在错误。今天就来谈谈我们在开发中遇到这种问题怎么解决。场景一,不知道大家有没有遇到过这种问题。今天,我想检查这个变量是否匹配枚举中的某个值。结果,TypeScript会为您喷射错误,如下所示。其实解决上面红色文字的方法有很多。第一种是使用@ts-ignore让错误消失。当然,这种方法是非常糟糕的。这相当于告诉TypeScript不要检查以下行。这个时候,我可能会想到另一种方法。上面的报错信息是male还没有赋值到GENDER这个类型,所以我只需要强制他赋值这个类型,像这样:但是这样写还是不好,也就是说你强制改变这个变量使得这个变量失去了灵活性。接下来,我们将介绍一个更好用的方法,让我们继续往下看吧!TypeGuard首先介绍一下TypeGuard。TypeGuard,顾名思义,就是类型保护。TypeScript会因为类型不同而报错,所以只要我们创建一个类型保护,让TypeScript知道这个变量必须匹配我枚举中的一个值,那么就不会出现红色字母了,通常TypeGuard会写成一个函数是这样的:constassertsIsGender=(gender:any):genderisGENDER=>{returnObject.values(GENDER).includes(gender)}这时候我们可以发现变量gender从string类型变成了GENDERtype所以即使我无聊再对includes做判断,TypeScript也不会报错。这里,我在给变量赋值之前,给变量赋了字符串类型。这个动作非常重要。如果不先给变量类型赋值,再赋值,变量就不能顺利改变类型。场景2不知道大家有没有遇到过API返回的数据,会因为数据对应的enum值不同而报错,如下:有了上面TypeGuard的概念,此时的读者必须知道必须要写一个函数来处理这个错误信息:确实错误信息没有了,但是奇怪的是性别变成了never类型,而这是TypeGuard会实现的类型保护机制叫做:缩小。类型缩小(Narrowing)Narrowing翻译成白话就是类型缩小。在TypeScript的世界里,每个枚举基本上都是独立存在的,彼此之间没有交集。关系图如下:所以需要两个枚举类型之间的类型转换很容易产生一个可能不存在的类型。对于可能不存在的类别,TypeScript将此类型定义为never。这时候,当我们使用类型保护技术时,TypeScript会自动将类型缩小为永不类型,而不是自动转换为另一个枚举。当然,如果你聪明的话,你可能会这样想:那么我只需要将函数return定义为另一个枚举,这样我就可以确保我的TypeGuard的结果被类型转换为我想要的枚举,像这样:看起来写起来没什么问题,我们要的结果从类型缩小变成了类型转换,但这其实不符合TypeGuard的精神。毕竟TypeGuard需要做的是类型检查而不是类型转换,而如果我们要做的是类型转换,这样写也会让这个函数的复用性不高,所以我们会引入更好的类型转换接下来的方法。Mapperenum首先我们可以考虑如何让类型转换可以被复用。我们不妨简化一下思路,即创建一个函数将类型A转换为类型B。这时候我们就必须使用TypeScript中的Generics泛型是一个trick,像这样:constcreateEnumMapper=(mapping:T)=>(值:keyofT|null):T[keyofT]|undefined=>{返回值===null?undefined:mapping[value]}createEnumMapper函数是柯里化函数。第一个变量被传递给枚举本身。这时候TypeScript的Generics就会知道我的T是和枚举本身相关的。为了让这个泛型正确映射两个枚举,我们必须首先创建一个对象来对两个枚举的键值进行配对,如下所示:constmapper={[BE_GENDER.MALE]:FE_GENDER.MALE,[BE_GENDER.FEMALE]:FE_GENDER.FEMALE}因为上面的mapper是把enum的值作为key的,所以我们只要把data的值带进来就可以直接转换,如下:这时候可以发现我们已经成功了converted将BE_GENDER类型的值转换为FE_GENDER类型的值,不需要使用TypeGuard的概念。总结今天我介绍了TypeScript中用于检查类型的方法。如果以后读者遇到类似的问题,不妨使用TypeGuard多检查一下,而不是直接使用@ts-ignore或者as。除了介绍类型检查,还介绍了如何进行类型转换。希望这些方法能让读者在以后的使用中没有太多问题。