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

UtilityTypes

时间:2023-03-11 22:57:44 科技观察

forTypeScriptLearning转载本文请联系xyz编程日记公众号。TypeScriptLearningTS的UtilityTypes全局内置了很多UtilityTypes,可以大大提高我们的开发效率。所以这篇文章是详细的介绍,了解,掌握。Partial功能:它将Type中的所有属性设为可选,并返回给定类型Type的子集。例:interfaceTodo{title:string;description:string;}//场景:只想更新toTo的部分属性,使用Partial更优雅functionupdateTodo(todo:Todo,fieldsToUpdate:Partial){return{...todo,...fieldsToUpdate};}consttodo1={title:"organizedesk",description:"clearclutter",};consttodo2=updateTodo(todo1,{description:"throwouttrash",});让我们看看Partial是如何实现的:/***MakeallpropertiesinToptional*/typePartial={[PinkeyofT]?:T[P];};上述定义涉及的知识点:泛型keyof操作符:获取T的所有key[PinkeyofT]:遍历T的所有key,映射类型,索引签名?:可选Required功能:Required相反上述Partial,构造返回一个新类型,其中需要Type的所有属性。例子:interfaceProps{a?:number;b?:string;}constobj:Props={a:5};constobj2:Required={a:5};//属性'b'ismissingintype'{a:number;}'butrequiredintype'Required'。我们看Required背后的实现:/***MakeallpropertiesinTrequired*/typeRequired={[PinkeyofT]-?:T[P];};上述定义涉及的知识点:TS2.8中改进了对映射类型修饰符的支持。在TS2.8之前,支持添加readonly和?修饰符映射类型的属性,但它没有提供删除修饰符的能力。默认情况下,其修饰符与映射类型一致。如果您有兴趣,可以查看此PR及其修复问题。现在映射类型支持添加或删除只读或?通过+或-修饰符。我们来看一个例子:typeA={readonlya?:number,b:string};typeMockRequired={-readonly[PinkeyofT]-?:T[P]//-?};consttest:MockRequired={//希望a是必填的a:10,b:'b'};test.a=20;//希望a可以修饰一下,这样我们就可以理解-?的意思了。Readonly功能:将Type的所有属性设置为只读。示例:interfaceTodo{title:string;}consttodo:Readonly={title:"Deleteinactiveusers",};todo.title="Hello";//不能分配给'title'因为它是只读属性。我们来看看Readonly背后的实现:/***MakeallpropertiesinTreadonly*/typeReadonly={readonly[PinkeyofT]:T[P];};有了上面的知识就比较容易理解了。您只需要知道映射类型支持修饰符readonly和?。另外,这里补充一下,readonly的含义和JS中const不可修改的意思是一样的,意思是不能被重写(rewriteassignment)。这个方法很适合Object.freeze的定义:functionfreeze(obj:Type):Readonly;Record作用:构建一个对象类型,其key来自Keys,其key对应的值为Type。所以这种方法非常适合将一种类型的属性映射到另一种类型。示例:interfaceCatInfo{age:number;breed:string;}typeCatName="miffy"|"boris"|"mordred";constcats:Record={miffy:{age:10,breed:"Persian"},boris:{age:5,breed:"MaineCoon"},mordred:{age:16,breed:"BritishShorthair"},};cats.boris;//(property)boris:CatInfo我们看看Record背后的定义./***ConstructatypewithasetofpropertiesKoftypeT*/typeRecord={[PinK]:T;};上面涉及的新知识点:keyofany。先看一段代码:typeA=keyofany;typeEqualA=string|number|symbol;//A其实等价于EqualAtypeIs=AextendsEqualA?true:false;constis:Is=false;//Type'false'isnotassignabletotype'真的'。所以如果我们这样使用,会提示错误:interfaceCatInfo{age:number;breed:string;}typeCatName="miffy"|"boris"|"mordred"|false;//false导致constcats:Record={//Error:Type'string|boolean'doesnotsatisfytheconstraint'string|number|symbol'.Type'boolean'isnotassignabletotype'string|number|symbol'.miffy:{age:10,breed:"波斯"},boris:{age:5,breed:"MaineCoon"},mordred:{age:16,breed:"BritishShorthair"},};Pick需要Keys的类型:字符串字面量或并集字符串文字。功能:构建并返回一个新类型,该类型根据Keys从类型Type中选择需要的属性。代码示例:interfaceTodo{title:string;description:string;completed:boolean;}typeTodoPreview=Pick;consttodo:TodoPreview={//只需要Keys:title和completedtitle:"Cleanroom",已完成:假,};待办事项;另外再看看背后的实现:这里没有新的知识点。/***FromT,pickasetofpropertieswhosekeysareintheunionK*/typePick={[PinK]:T[P];};省略这里就不重复介绍了,可以看我之前的文章:省略TypeScript学习。Exclude作用:从Type中排除可以赋值给ExcludedUnion的类型。示例:typeT0=排除<"a"|"b"|"c","a">;//typeT0="b"|"c"typeT1=排除<"a"|"b"|"c","a"|"b">;//typeT1="c"typeT2=Excludevoid),Function>;//typeT2=string|number我们来看Exclude背后的实现:/***ExcludefromTthosetypesthatareassignabletoU*/typeExclude=TextendsU?never:T;涉及知识点:TextendsU?never:T这里的extends和类的extends不一样,这里指的是条件类型。这里就不展开太多了,重点通过一个分布式条件类型的概念来理解上面Exclude的写法。typeA='a'|'b'|'c';typeB='a';typeC=Exclude;//'b'|'c';//AextendsB?never:A等价于('a'|'b'|'c')extendsB?never:('a'|'b'|'c')等同于以下typeD=('a'extendsB?never:'a')|('b'extendsB?never:'b')|('c'extendsB?never:'c');//'b'|'c';Extract作用:从Type中取出并赋值给Union的类型。示例:typeT0=Extract<"a"|"b"|"c","a"|"f">;//typeT0="a"typeT1=Extractvoid),Function>;//typeT1=()=>void我们看一下Extract背后的定义:/***ExtractfromTthosetypesthatareassignabletoU*/typeExtract=TextendsU?T:never;所有你可以看到Extract就像Exclude一样否定差异。NonNullable功能:排除类型Type中的null和undefined。例子:typeT0=NonNullable;//typeT0=string|numbertypeT1=NonNullable;//typeT1=string[]看NonNullable的定义:/***ExcludenulllandundefinedfromT*/typeNonNullable=Textendsnull|undefined?never:T;我们可以看到其实就是上面分布式条件类型extends的应用。Parameters作用:根据Type类型的参数构建一个新的元组类型。示例:declarefunctionf1(arg:{a:number;b:string}):void;typeT0=Parameters<()=>string>;//typeT0=[]typeT1=Parameters<(s:string)=>void>;//typeT1=[s:string]typeT2=Parameters<(arg:T)=>T>;//typeT2=[arg:unknown]typeT3=Parameters;//typeT3=[arg:{//a:number;//b:string;//}]typeT4=Parameters;//typeT4=unknown[]typeT5=Parameters;//typeT5=nevertypeT6=Parameters;//Type'string'doesnotsatisfytheconstraint'(...args:any)=>any'.typeT6=nevertypeT7=Parameters;//Type'Function'doesnotsatisfytheconstraint'(...args:any)=>any'.//Type'Function'providesnomatchforthesignature'(...args:any):any'.//typeT7=never我们来看看参数背后的实现。/***获取函数类型元组的参数*/typeParametersany>=Textends(...args:inferP)=>any?P:never;涉及知识点:Textends(...args:any)=>any定义了Parameters的泛型约束,兼容当前所有函数的类型定义。inferP:用于表示要推断的函数参数。Textends(...args:inferP)=>任何?P:never:表示如果T可以赋值给(...args:inferP)=>any,则结果为(...args:inferP)=>任何类型的参数都是P,否则返回从来没有。详细了解info,推荐深入了解typescript-info。ConstructorParameters作用:根据构造函数类型Type的参数类型构造元组或数组类型(如果Type不是函数,则从不构造)。示例:typeT0=ConstructorParameters;//typeT0=[message?:string]typeT1=ConstructorParameters;//typeT1=string[]typeT2=ConstructorParameters;//typeT2=[pattern:string|RegExp,flags?:string]typeT3=ConstructorParameters;//typeT3=unknown[]看它的ConstructorParameters定义:(...args:inferP)=>any?P:never;ConstructorParameters的定义与Parameters的定义几乎相同,区别在于前者是表达构造函数签名的定义。常见的构造函数类型签名是:基于类型或接口。typeSomeConstructor={new(s:string):SomeObject;};functionfn(ctor:SomeConstructor){returnnewctor("hello");}interfaceCallOrConstruct{new(s:string):Date;(n?:number):number;}ReturnType作用:根据函数Type的返回值类型创建一个新的类型。示例:declarefunctionf1():{a:number;b:string};typeT0=ReturnType<()=>string>;//typeT0=stringtypeT4=ReturnType;//typeT4={//a:number;//b:string;//}源码定义:/***Obtainthereturntypeoffunctiontype*/typeReturnTypeany>=Textends(...args:any)=>inferR?R:任何;可以看到原理和前面的差不多,不同的是infer推理的位置不一样。InstanceType功能:根据函数类型Type的构造函数类型构造一个新的类型。示例:classC{x=0;y=0;}typeT0=InstanceType;//typeT0=CtypeT1=InstanceType;//typeT1=any源定义:/***Obtainthereturturntypeofaconstructorfunctiontype*/typeInstanceTypeany>=Textendsabstractnew(...args:any)=>inferR?R:any;通过对比发现,InstanceType和ReturnType的区别在于它多了函数构造签名定义,与ConstructorParameters的区别在于它推断的不是参数类型,而是返回值类型。ThisParameterType功能:获取函数类型Type中的this类型。如果没有返回未知。functiontoHex(this:Number){returnthis.toString(16);}functionnumberToString(n:ThisParameterType){//n:numberreturntoHex.apply(n);}源码定义:/***Extractsthetypeofthe'this'parameteroffunctiontype,或'unknown'如果函数类型没有'this'参数。*/typeThisParameterType=Textends(this:inferU,...args:any[])=>any?U:unknown;如果想知道这个在函数中是怎么定义的,建议去官网看看。OmitThisParameter作用:去除函数类型Type中参数的this。示例:functiontoHex(this:Number){returnthis.toString(16);}constfiveToHex:OmitThisParameter=toHex.bind(5);//constfiveToHex:()=>stringconsole.log(fiveToHex());源定义:/***从函数类型中删除“this”参数。*/typeOmitThisParameter=unknownextendsThisParameterType?T:Textends(...args:inferA)=>inferR?(...args:A)=>R:T;unknownextendsThisParameterType:如果T函数参数中没有this,则直接返回T。否则,Textends(...args:inferA)=>inferR?(...args:A)=>R:T;,如果T是后者的子类型,则返回新的函数,函数参数为inferA推导,返回值为inferR。否则返回T。参考到官网结束。