当前位置: 首页 > Web前端 > JavaScript

精读《Typescript 4.4》

时间:2023-03-26 23:31:46 JavaScript

Typescript4.4正式发布!距离Typescript4.5发布还有三个月,赶快学习吧!本周精修文章:announcing-typescript-4-4overview更智能的自动类型收窄类型收窄功能非常方便,可以让Typescript尽量像Js一样自动智能判断类型,从而免去类型定义的工作,让你的Typescript更像Js。其实这个功能早就有了。我们的精读里面已经介绍过《Typescript2.0 - 2.9》。当时使用的名词是自动类型推导。这一次,使用更精确的术语自动类型缩小,因为只有类型缩小是安全的,例如:functionfoo(arg:unknown){if(typeofarg==="string"){//我们现在知道'arg'是一个字符串。console.log(arg.toUpperCase());}}而在Typescript4.4之前的版本中,如果我们将这个判断赋值给一个变量,然后在if分支中使用,则无法正常缩小类型:functionfoo(arg:unknown){constargIsString=typeofarg==="细绳”;如果(argIsString){console.log(arg.toUpperCase());//~~~~~~~~~~~//错误!“未知”类型不存在属性“toUpperCase”。}}这个问题在Typescript4.4已经解决了,其实是加深了这种类型收窄的判断逻辑,即无论判断写在哪里,都能生效。因此,下面的解构用法判断也可以推断类型缩小:typeShape=|{种类:“圆”,半径:数字}|{种类:“正方形”,sideLength:数字};functionarea(shape:Shape):number{//首先提取出'kind'字段。const{kind}=形状;if(kind==="circle"){//我们知道这里有一个圆圈!返回Math.PI*形状。半径**2;}else{//我们知道这里剩下一个正方形!返回shape.sideLength**2;}}不仅仅是单一的判断,Typescript4.4还支持复合类型推导:if(mustDoWork){//我们可以访问'inputA'和'inputB'上的'string'属性!constupperA=inputA.toUpperCase();constupperB=inputB.toUpperCase();//...}}mustDoWork为true的分支表示inputA和inputB都缩小为字符串类型。这种深度判断还体现在对一个有类型判断的变量进行重新计算,生成的变量也有类型判断的功能:functionf(x:string|number|boolean){constisString=typeofx==="string";constisNumber=typeofx==="数字";常量isStringOrNumber=isString||是数字;如果(isStringOrNumber){x;//'x'的类型是'string|数字'。}否则{x;//'x'的类型是'boolean'。}}可以看到,我们几乎可以像写Js一样写Typescript,而且4.4支持了大部分直观的推导,非常方便。但需要注意的是,Typescript毕竟不是运行时,无法实现更彻底的自动推理,但足以支持大部分场景。下标支持Symbol和模板字符串类型判断原来我们定义一个下标访问的对象是这样的:interfaceValues{[key:string]:number}现在也支持Symbolpull:interfaceColors{[sym:symbol]:number;}constred=Symbol("red");constgreen=Symbol("green");constblue=Symbol("blue");letcolors:Colors={};colors[red]=255;//允许分配一个数字letredVal=colors[red];//'redVal'的类型为'number'colors[blue]="dabadee";//错误:类型“string”不可分配给类型“number”。特定字符串模板也支持类型匹配。比如你想让data-开头的下标是一个独立的类型,你可以这样定义:interfaceOptions{width?:number;height?:number;}leta:Options={width:100,height:100,"data-blah":true,//错误!'data-blah'未在'Options'中声明。};interfaceOptionsWithDataPropsextendsOptions{//允许任何以'data-'开头的属性。[optName:`data-${string}`]:unknown;}letb:OptionsWithDataProps={width:100,height:100,"data-blah":true,//有效!“未知属性y":true,//Error!'unknown-property'wasn'tdeclaredin'OptionsWithDataProps'.};这对HTML数据属性很有帮助,也支持联合类型定义,下面两种类型的定义方式是等价的等价于:interfaceData{[optName:string|symbol]:any;}//等价于interfaceData{[optName:string]:any;[optName:symbol]:any;}更严格的错误捕获类型来自未知type之前,Typescript默认使用any作为抛出错误的类型,毕竟没有人知道抛出错误的类型是什么:try{//谁知道这会抛出什么...{//err:anyconsole.error(err.message);//允许,因为'any'err.thisWillProbablyFail();//允许,因为'any':(}谁知道这会抛出什么...这有意思的是,一个函数的任何地方都可能出现运行时错误,静态分析根本解决不了,所以无法自动推导出错误类型,所以你只能使用任何。在Typescript4.4的--useUnknownInCatchVariables或--strict模式中,两者都将使用unknown作为捕获错误的默认类型。与不存在的类型never相比,unknown只是不知道它是什么类型,所以不能像any一样作为任意类型使用,但是我们可以随意推断为任意类型:try{executeSomeThirdPartyCode();}catch(err){//错误:未知//错误!“未知”类型不存在属性“消息”。console.error(err.message);//有效!我们可以将“错误”从“未知”缩小为“错误”。if(errinstanceofError){console.error(err.message);如果你觉得这样做很麻烦,你可以重新声明类型为any:错误(错误信息);//Worksagain!}但这实际上是不合适的,因为即使考虑运行时因素,理论上也可能会出现意想不到的错误,所以对错误过于自信的类型推断是不合适的,最好保留其未知类型和处理所有可能的边缘情况。明确可选属性对象可选属性的类型描述有歧义,如:interfacePerson{name:string,age?:number;}其实Typescript定义其类型为:interfacePerson{name:string,age?:数字|undefined;}为什么这样定义?因为很多时候,没有这个key,就相当于这个key的值是undefined的表现。但是比如在Object.keys场景下,这两种表现是不等价的,所以理论上age?:number的准确表达方式是:要么没有age,要么有age且type为number,即比如说,下面的写法应该是错误的://With'exactOptionalPropertyTypes'on:constp:Person={name:"Daniel",age:undefined,//错误!undefined不是数字};同时打开--exactOptionalPropertyTypes和-在Typescript4.4中-strictNullChecks将生效。仔细想想也是有道理的。既然defined类型不是undefined,即使对象是optional类型,也不能认为赋值undefined是合理的,因为age?:number的心理预期是要么没有这个key,要么有but类型是数字,所以当Object.keys找到keyage时,值应该是数字。支持静态块Typescript4.4支持类静态块,可以在代码块作用域内访问私有变量。还有一些性能提升和体验优化杂项就不一一列举了。有兴趣的可以直接参考原文档:perf-improvements。总结从Typescript4.4的特性可以看出Typescript正在朝着“更加原生JS亲和”的方向努力,这无疑会让Typescript越来越好用。如果你对更多新特性感兴趣,可以查看Typescript4.5发布计划。讨论地址为:精读《Typescript 4.4》·第348期·dt-fe/weekly想参与讨论的请戳这里,每周都有新话题,周末或周一发布。前端精读——帮你过滤靠谱的内容。关注前端精读微信公众号版权声明:免费转载-非商业-非衍生保留属性(CreativeCommons3.0License)