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

看完这些问题,同事们发现TS跨界型还没介绍呢!

时间:2023-03-18 00:49:00 科技观察

TypeScript的交集类型你掌握了吗?如果有,你知道这些类型相交后的结果吗?如果你不知道,也许看完这篇文章你就会明白了。//非对象类型交叉运算类型N0=string&number;输入N1=任意&1;输入N2=任何&从不;//对象类型交叉操作typeA={kind:'a',foo:string};typeB={kind:'b',foo:number};typeC={kind:'c',foo:number};类型AB=A&B;类型BC=B&C;//函数类型交叉运算typeF1=(a:string,b:string)=>void;typeF2=(a:number,b:number)=>void;typeFn=F1&F2在学习TypeScript的过程中,您可以将类型理解为一系列值的集合。例如,您可以将数字类型视为所有数字的集合。1.0和68属于这个集合,但是“阿宝哥”不属于这个集合,因为它属于字符串类型。同样,对于对象类型,我们也可以理解为对象的集合。比如上面代码中的Point类型,表示它包含x和y属性,属性值的类型都是数字类型对象的集合。Named类型表示具有名称属性的对象集合,属性值的类型为字符串类型对象。接口点{x:数字;y:number;}interfaceNamed{name:string;}在集合论中,假设A和B是两个集合,一个集合由属于集合A和集合B的所有元素组成,称为集合A和集合B的交集.当我们对Point类型和Named类型进行交集操作时,会生成一个新的类型。此类型中包含的对象都是点和命名的。TypeScript为我们提供了交集运算符,可以对多种类型进行交集运算,得到的新类型也称为交集类型。下面简单介绍一下交叉算子,它满足以下特点:唯一性:A&A等价于A满足交换律:A&B等价于B&A满足结合律:(A&B)&C等价到A&(B&C)父类型收敛:如果B是A的父类型,那么A&B会收敛到类型AtypeA0=1&number;//1typeA1="1"&string;//"1"typeA2=true&boolean;//真类型A3=任何&1;//任意类型A4=任意&布尔值;//任何类型A5=任何&从不;//上面代码中的never,any类型和never类型比较特殊。除never之外的任何类型都与any交叉,结果为any。介绍完交集运算符,我们来看看Point类型和Named类型交集后会生成什么类型???interfacePoint{x:number;y:number;}interfaceNamed{name:string;}typeNamedPoint=Point&Named//{//.x:数字;//。y:数字;//。名称:字符串;//。在上面的代码中,新生成的NamedPoint类型将同时包含x和y以及名称属性。但是如果执行交叉操作的多个对象类型包含相同的属性但属性的类型不一致怎么办?接口X{c:字符串;d:字符串;}接口Y{c:数字;e:字符串}typeXY=X&Y;typeYX=Y&X;上述代码中,interfaceX和interfaceY都包含相同的属性c,只是类型不一致。这样的话,XY类型或者YX类型的属性c的类型可以是string还是number类型呢?下面我们来验证一下:letp:XY={c:"c",d:"d",e:"e"};//Errorletq:YX={c:6,d:"d",e:"e"};//Error为什么接口X和接口Y进行交叉操作后属性c的值会变成never类型?这是因为属性c运算后的类型是string&number,即属性c的类型既可以是string类型,也可以是number类型。显然这个类型是不存在的,所以c属性运算后的类型就是never类型。在前面的示例中,碰巧接口X和接口Y中的c属性的类型都是原始数据类型。那么如果不同的对象类型包含相同的属性,而属性类型是非基本数据类型,会发生什么情况呢?我们来看一个具体的例子:interfaceD{d:boolean;}接口E{e:字符串;}interfaceF{f:数字;}接口A{x:D;}接口B{x:E;}接口C{x:F;}typeABC=A&B&C;letabc:ABC={//okx:{d:true,e:'Abaoge',f:666}};从上面的结果我们可以看出,在对多个类型进行交叉操作时,如果存在相同的属性,并且属性类型是对象类型,那么这些属性会按照相应的规则进行合并。但需要注意的是,在对对象类型进行交叉操作时,如果将对象中的同一个属性视为可识别属性,即该属性的类型为字面量类型或由字面量类型组成的联合类型,那么最终的操作结果永远不会是type:typeA={kind:'a',foo:string};typeB={kind:'b',foo:number};typeC={kind:'c',foo:数字};类型AB=A&B;//从不输入BC=B&C;//never在上面的代码中,A、B、C三种对象类型都包含kind属性,并且属性的类型都是字符串字面量类型,所以类型AB和BC最终都是never类型。接下来我们继续看一个例子:typeFoo={name:string,age:number}typeBar={name:number,age:number}typeBaz=Foo&Bar//{//.name:never;//。年龄:数字;//}上面代码中,Baz类型是包含name属性和age属性的对象类型,其中name属性的类型为never类型,age属性的类型为number类型。但是如果把Foo类型中name属性的类型改成boolean类型,Baz类型就会变成never类型。这是因为boolean类型可以理解为由true和false字面量类型组成的联合类型。typeFoo={name:boolean,//真|falseage:number}typeBar={name:number,age:number}typeBaz=Foo&Bar//never其实除了对象类型可以进行交叉操作,函数类型Interleave操作也是可以的:typeF1=(a:字符串,b:字符串)=>void;输入F2=(a:数字,b:数字)=>void;让f:F1&F2=(a:字符串|数字,b:字符串|数字)=>{};f("你好","世界");//好的f(1,2);//好的f(1,"测试");//Errorfor上面代码中的函数调用语句中,只有f(1,"test")的调用语句会出错,对应的错误信息如下:没有匹配该调用的重载。重载1of2,'(a:string,b:string):void',抛出以下错误。“数字”类型的参数不可分配给“字符串”类型的参数。第二个重载(共2个)“(a:number,b:number):void”抛出以下错误。“字符串”类型的参数不可分配给“数字”类型的参数。ts(2769)根据上面的错误信息,我们可以了解到TypeScript编译器会利用函数重载的特性来实现不同函数类型的交叉操作。为了解决上述问题,我们可以定义一个新的函数类型F3,具体如下:typeF1=(a:string,b:string)=>void;输入F2=(a:数字,b:数字)=>void;输入F3=(a:数字,b:字符串)=>void;让f:F1&F2&F3=(a:字符串|数字,b:字符串|数字)=>{};f("你好","世界");//好的f(1,2);//好的f(1,"测试");//Ok在掌握了交集类型后,结合前面文章介绍的映射类型,我们可以根据工作需要实现一些自定义的工具类型。例如,实现一个PartialByKeys工具类型,使对象类型中指定的键成为可选的。输入User={id:number;名称:字符串;age:number;}typePartialByKeys=Simplify<{[PinK]?:T[P]}&Pick>>类型U1=PartialByKeys<用户,“id”>类型U2=PartialByKeys<用户,“id”|"name">那么如果你实现一个RequiredByKeys工具类型,它是用来指定对象类型的keys变成强制的,你知道如何实现吗?如果你知道答案,你喜欢用这种形式来学习TS吗?