Typescript4.docx

上传人:b****8 文档编号:30359475 上传时间:2023-08-13 格式:DOCX 页数:14 大小:19.74KB
下载 相关 举报
Typescript4.docx_第1页
第1页 / 共14页
Typescript4.docx_第2页
第2页 / 共14页
Typescript4.docx_第3页
第3页 / 共14页
Typescript4.docx_第4页
第4页 / 共14页
Typescript4.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

Typescript4.docx

《Typescript4.docx》由会员分享,可在线阅读,更多相关《Typescript4.docx(14页珍藏版)》请在冰豆网上搜索。

Typescript4.docx

Typescript4

1引言

随着Typescript4Beta的发布,又带来了许多新功能,其中VariadicTupleTypes解决了大量重载模版代码的顽疾,使得这次更新非常有意义。

2简介

可变元组类型

考虑concat场景,接收两个数组或者元组类型,组成一个新数组:

functionconcat(arr1,arr2){

return[...arr1,...arr2];

}

如果要定义concat的类型,以往我们会通过枚举的方式,先枚举第一个参数数组中的每一项:

functionconcat<>(arr1:

[],arr2:

[]):

[A];

functionconcat(arr1:

[A],arr2:

[]):

[A];

functionconcat(arr1:

[A,B],arr2:

[]):

[A,B];

functionconcat(arr1:

[A,B,C],arr2:

[]):

[A,B,C];

functionconcat(arr1:

[A,B,C,D],arr2:

[]):

[A,B,C,D];

functionconcat(arr1:

[A,B,C,D,E],arr2:

[]):

[A,B,C,D,E];

functionconcat(arr1:

[A,B,C,D,E,F],arr2:

[]):

[A,B,C,D,E,F];)

再枚举第二个参数中每一项,如果要完成所有枚举,仅考虑数组长度为6的情况,就要定义36次重载,代码几乎不可维护:

functionconcat(arr1:

[],arr2:

[A2]):

[A2];

functionconcat(arr1:

[A1],arr2:

[A2]):

[A1,A2];

functionconcat(arr1:

[A1,B1],arr2:

[A2]):

[A1,B1,A2];

functionconcat

arr1:

[A1,B1,C1],

arr2:

[A2]

):

[A1,B1,C1,A2];

functionconcat

arr1:

[A1,B1,C1,D1],

arr2:

[A2]

):

[A1,B1,C1,D1,A2];

functionconcat

arr1:

[A1,B1,C1,D1,E1],

arr2:

[A2]

):

[A1,B1,C1,D1,E1,A2];

functionconcat

arr1:

[A1,B1,C1,D1,E1,F1],

arr2:

[A2]

):

[A1,B1,C1,D1,E1,F1,A2];

如果我们采用批量定义的方式,问题也不会得到解决,因为参数类型的顺序得不到保证:

functionconcat(arr1:

T[],arr2,U[]):

Array;

在Typescript4,可以在定义中对数组进行解构,通过几行代码优雅的解决可能要重载几百次的场景:

typeArr=readonlyany[];

functionconcat(arr1:

T,arr2:

U):

[...T,...U]{

return[...arr1,...arr2];

}

上面例子中,Arr类型告诉TST与U是数组类型,再通过[...T,...U]按照逻辑顺序依次拼接类型。

再比如tail,返回除第一项外剩下元素:

functiontail(arg){

const[_,...result]=arg;

returnresult;

}

同样告诉TST是数组类型,且arr:

readonly[any,...T]申明了T类型表示除第一项其余项的类型,TS可自动将T类型关联到对象rest:

functiontail(arr:

readonly[any,...T]){

const[_ignored,...rest]=arr;

returnrest;

}

constmyTuple=[1,2,3,4]asconst;

constmyArray=["hello","world"];

//type[2,3,4]

constr1=tail(myTuple);

//type[2,3,...string[]]

constr2=tail([...myTuple,...myArray]asconst);

另外之前版本的TS只能将类型解构放在最后一个位置:

typeStrings=[string,string];

typeNumbers=[number,number];

//[string,string,number,number]

typeStrStrNumNum=[...Strings,...Numbers];

如果你尝试将[...Strings,...Numbers]这种写法,将会得到一个错误提示:

Arestelementmustbelastinatupletype.

但在Typescript4版本支持了这种语法:

typeStrings=[string,string];

typeNumbers=number[];

//[string,string,...Array]

typeUnbounded=[...Strings,...Numbers,boolean];

对于再复杂一些的场景,例如高阶函数partialCall,支持一定程度的柯里化:

functionpartialCall(f,...headArgs){

return(...tailArgs)=>f(...headArgs,...tailArgs);

}

我们可以通过上面的特性对其进行类型定义,将函数f第一个参数类型定义为有顺序的[...T,...U]:

typeArr=readonlyunknown[];

functionpartialCall

f:

(...args:

[...T,...U])=>R,

...headArgs:

T

){

return(...b:

U)=>f(...headArgs,...b);

}

测试效果如下:

constfoo=(x:

string,y:

number,z:

boolean)=>{};

//Thisdoesn'tworkbecausewe'refeedinginthewrongtypefor'x'.

constf1=partialCall(foo,100);

//~~~

//error!

Argumentoftype'number'isnotassignabletoparameteroftype'string'.

//Thisdoesn'tworkbecausewe'repassingintoomanyarguments.

constf2=partialCall(foo,"hello",100,true,"oops");

//~~~~~~

//error!

Expected4arguments,butgot5.

//Thisworks!

Ithasthetype'(y:

number,z:

boolean)=>void'

constf3=partialCall(foo,"hello");

//Whatcanwedowithf3now?

f3(123,true);//works!

f3();

//error!

Expected2arguments,butgot0.

f3(123,"hello");

//~~~~~~~

//error!

Argumentoftype'"hello"'isnotassignabletoparameteroftype'boolean'

值得注意的是,constf3=partialCall(foo,"hello");这段代码由于还没有执行到foo,因此只匹配了第一个x:

string类型,虽然后面y:

number,z:

boolean也是必选,但因为foo函数还未执行,此时只是参数收集阶段,因此不会报错,等到f3(123,true)执行时就会校验必选参数了,因此f3()时才会提示参数数量不正确。

元组标记

下面两个函数定义在功能上是一样的:

functionfoo(...args:

[string,number]):

void{

//...

}

functionfoo(arg0:

string,arg1:

number):

void{

//...

}

但还是有微妙的区别,下面的函数对每个参数都有名称标记,但上面通过解构定义的类型则没有,针对这种情况,Typescript4支持了元组标记:

typeRange=[start:

number,end:

number];

同时也支持与解构一起使用:

typeFoo=[first:

number,second?

:

string,...rest:

any[]];

Class从构造函数推断成员变量类型

构造函数在类实例化时负责一些初始化工作,比如为成员变量赋值,在Typescript4,在构造函数里对成员变量的赋值可以直接为成员变量推导类型:

classSquare{

//Previously:

implicitany!

//Now:

inferredto`number`!

area;

sideLength;

constructor(sideLength:

number){

this.sideLength=sideLength;

this.area=sideLength**2;

}

}

如果对成员变量赋值包含在条件语句中,还能识别出存在undefined的风险:

classSquare{

sideLength;

constructor(sideLength:

number){

if(Math.random()){

this.sideLength=sideLength;

}

}

getarea(){

returnthis.sideLength**2;

//~~~~~~~~~~~~~~~

//error!

Objectispossibly'undefined'.

}

}

如果在其他函数中初始化,则TS不能自动识别,需要用!

:

显式申明类型:

classSquare{

//definiteassignmentassertion

//v

sideLength!

:

number;

//^^^^^^^^

//typeannotation

constructor(sideLength:

number){

this.initialize(sideLength);

}

initialize(sideLength:

number){

this.sideLength=sideLength;

}

getarea(){

returnthis.sideLength**2;

}

}

短路赋值语法

针对以下三种短路语法提供了快捷赋值语法:

a&&=b;//a&&(a=b)

a||=b;//a||(a=b)

a?

?

=b;//a?

?

(a=b)

catcherrorunknown类型

Typescript4.0之后,我们可以将catcherror定义为unknown类型,以保证后面的代码以健壮的类型判断方式书写:

try{

//...

}catch(e){

//error!

//Property'toUpperCase'doesnotexistontype'unknown'.

console.log(e.toUpperCase());

if(typeofe==="string"){

//works!

//We'venarrowed'e'downtothetype'string'.

console.log(e.toUpperCase());

}

}

PS:

在之前的版本,catch(e:

unknown)会报错,提示无法为error定义unknown类型。

自定义JSX工厂

TS4支持了jsxFragmentFactory参数定义Fragment工厂函数:

{

"compilerOptions":

{

"target":

"esnext",

"module":

"commonjs",

"jsx":

"react",

"jsxFactory":

"h",

"jsxFragmentFactory":

"Fragment"

}

}

还可以通过注释方式覆盖单文件的配置:

//Note:

thesepragmacommentsneedtobewritten

//withaJSDoc-stylemultilinesyntaxtotakeeffect.

/**@jsxh*/

/**@jsxFragFragment*/

import{h,Fragment}from"preact";

letstuff=(

<>

Hello

);

以上代码编译后解析结果如下:

//Note:

thesepragmacommentsneedtobewritten

//withaJSDoc-stylemultilinesyntaxtotakeeffect.

/**@jsxh*/

/**@jsxFragFragment*/

import{h,Fragment}from"preact";

letstuff=h(Fragment,null,h("div",null,"Hello"));

其他升级

其他的升级快速介绍:

构建速度提升,提升了--incremental+--noEmitOnError场景的构建速度。

支持--incremental+--noEmit参数同时生效。

支持@deprecated注释,使用此注释时,代码中会使用删除线警告调用者。

局部TSServer快速启动功能,打开大型项目时,TSServer要准备很久,Typescript4在VSCode编译器下做了优化,可以提前对当前打开的单文件进行部分语法响应。

优化自动导入,现在package.jsondependencies字段定义的依赖将优先作为自动导入的依据,而不再是遍历node_modules导入一些非预期的包。

除此之外,还有几个BreakChange:

lib.d.ts类型升级,主要是移除了document.origin定义。

覆盖父Class属性的getter或setter现在都会提示错误。

通过delete删除的属性必须是可选的,如果试图用delete删除一个必选的key,则会提示错误。

3精读

Typescript4最大亮点就是可变元组类型了,但可变元组类型也不能解决所有问题。

拿笔者的场景来说,函数useDesigner作为自定义ReactHook与useSelector结合支持connectredux数据流的值,其调用方式是这样的:

constnameSelector=(state:

any)=>({

name:

state.nameasstring,

});

constageSelector=(state:

any)=>({

age:

state.ageasnumber,

});

constApp=()=>{

const{name,age}=useDesigner(nameSelector,ageSelector);

};

name与age是Selector注册的,内部实现方式必然是useSelector+reduce,但类型定义就麻烦了,通过重载可以这么做:

import*asReactfrom'react';

import{useSelector}from'react-redux';

typeFunction=(...args:

any)=>any;

exportfunctionuseDesigner();

exportfunctionuseDesigner

t1:

T1

):

ReturnType;

exportfunctionuseDesigner

t1:

T1,

t2:

T2

):

ReturnType&ReturnType;

exportfunctionuseDesigner<

T1extendsFunction,

T2extendsFunction,

T3extendsFunction

>(

t1:

T1,

t2:

T2,

t3:

T3,

t4:

T4,

):

ReturnType&

ReturnType&

ReturnType&

ReturnType&

;

exportfunctionuseDesigner<

T1extendsFunction,

T2extendsFunction,

T3extendsFunction,

T4extendsFunction

>(

t1:

T1,

t2:

T2,

t3:

T3,

t4:

T4

):

ReturnType&

ReturnType&

ReturnType&

ReturnType&

;

exportfunctionuseDesigner(...selectors:

any[]){

returnuseSelector((state)=>

selectors.reduce((selected,selector)=>{

return{

...selected,

...selector(state),

};

},{})

)asany;

}

可以看到,笔者需要将useDesigner传入的参数通过函数重载方式一一传入,上面的例子只支持到了三个参数,如果传入了第四个参数则函数定义会失效,因此业界做法一般是定义十几个重载,这样会导致函数定义非常冗长。

但参考TS4的例子,我们可以避免类型重载,而通过枚举的方式支持:

typeFunc=(state?

:

any)=>any;

typeArr=readonlyFunc[];

constuseDesigner=

...selectors:

T

):

ReturnType&

ReturnType&

ReturnType&

ReturnType=>{

returnuseSelector((state)=>

selectors.reduce((selected,selector)=>{

return{

...selected,

...selector(state),

};

},{})

)asany;

};

可以看到,最大的变化是不需要写四遍重载了,但由于场景和concat不同,这个例子返回值不是简单的[...T,...U],而是reduce的结果,所以目前还只能通过枚举的方式支持。

当然可能存在不用枚举就可以支持无限长度的入参类型解析的方案,因笔者水平有限,暂未想到更好的解法,如果你有更好的解法,欢迎告知笔者。

4总结

Typescript4带来了更强类型语法,更智能的类型推导,更快的构建速度以及更合理的开发者工具优化,唯一的几个BreakChange不会对项目带来实质影响,期待正式版的发布。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1