
局部类型、联合类型、交叉类型1、局部类型2、联合类型2.1、联合类型字面量2.2、联合类型的类型成员2.2.1、属性签名2.2.2、索引签名2.2.3、调用签名与构造签名3、交叉类型3.1、交叉类型字面量3.1.1、成员类型的运算3.1.2、原生类型3.2、交叉类型的类型成员3.2.1、属性签名3.2.2、索引签名3.2.3、调用签名与构造签名3.3、交叉类型与联合类型3.3.1、优先级3.3.2、分配率性质1、局部类型TypeScript同样支持声明具有块级作用域的局部类型主要包括局部枚举类型。局部类类型。局部接口类型。局部类型别名。下例中在函数f内部分别声明了以上几种局部类型functionfT(){enumE{A,B,}classC{x:string|undefined;}//允许带有泛型参数interfaceIT{x:T;}//可以引用其他局部类型typeAE.A|E.B;}此例中枚举类型E、类类型C、接口类型I和类型别名A都是局部类型。局部类型也允许带有类型参数并且可以引用外层作用域中的类型参数。类似于let声明和const声明局部类型拥有块级作用域。例如下例中在if分支和else支持中均声明了接口T它们仅在各自所处的块级作用域内生效。因此这两个接口T不会相互影响并且if分支中的代码也无法引用else分支中的接口T。示例如下functionf(x:boolean){if(x){interfaceT{x:number;}constv:T{x:0};}else{interfaceT{x:string;}constv:T{x:foo};}}2、联合类型联合类型由一组有序的成员类型构成。联合类型表示一个值的类型可以为若干种类型之一。例如联合类型“string | number”表示一个值的类型既可以为string类型也可以为number类型。联合类型通过联合类型字面量来定义。2.1、联合类型字面量联合类型由两个或两个以上的成员类型构成各成员类型之间使用竖线符号“|”分隔。示例如下typeNumericTypenumber|bigint;此例中定义了一个名为NumericType的联合类型。该联合类型由两个成员类型组成即number类型和bigint类型。若一个值既可能为number类型又可能为bigint类型那么我们说该值的类型为联合类型“number | bigint”。联合类型的成员类型可以为任意类型如原始类型、数组类型、对象类型以及函数类型等。示例如下typeTboolean|string[]|{x:number}|(()void);如果联合类型中存在相同的成员类型那么相同的成员类型将被合并为单一成员类型。例如下例中的类型别名T0和T1都表示boolean类型类型别名T2和T3都表示同一种联合类型“boolean |string”typeT0boolean;// booleantypeT1boolean|boolean;// booleantypeT2boolean|string;// boolean | stringtypeT3boolean|string|boolean;// boolean | string联合类型是有序的成员类型的集合。在绝大部分情况下成员类型满足类似于数学中的“加法交换律”即改变成员类型的顺序不影响联合类型的结果类型。下例中虽然联合类型T0与T1中成员类型的顺序不同但是T0与T1表示相同的类型typeT0string|number;typeT1number|string;联合类型中的类型成员同样满足类似于数学中的“加法结合律”。对部分类型成员使用分组运算符不影响联合类型的结果类型。例如下例中的T0和T1类型是同一个类型typeT0(boolean|string)|number;typeT1boolean|(string|number);联合类型的成员类型可以进行化简。假设有联合类型“U T0 |T1”如果T1是T0的子类型那么可以将类型成员T1从联合类型U中消去。最后联合类型U的结果类型为“U T0”。例如有联合类型“boolean | true | false”。其中true类型和false类型是boolean类型的子类型因此可以将true类型和false类型从联合类型中消去。最终联合类型“boolean | true | false”的结果类型为boolean类型。示例如下// true 和 false 类型是 boolean 类型的子类型typeT0boolean|true|false;// 所以T0等同于 T1typeT1boolean;2.2、联合类型的类型成员像接口类型一样联合类型作为一个整体也可以有类型成员只不过联合类型的类型成员是由其成员类型决定的。2.2.1、属性签名若联合类型U中的每个成员类型都包含一个同名的属性签名M那么联合类型U也包含属性签名M。例如有如下定义的Circle类型与Rectangle类型以及由这两个类型构成的联合类型ShapeinterfaceCircle{area:number;radius:number;}interfaceRectangle{area:number;width:number;height:number;}typeShapeCircle|Rectangle;此例中因为Circle类型与Rectangle类型均包含名为area的属性签名类型成员所以联合类型Shape也包含名为area的属性签名类型成员。因此允许访问Shape类型上的area属性。示例如下typeShapeCircle|Rectangle;declareconsts:Shape;s.area;// number因为radius、width和height类型成员不是 Circle类型和Rectangle类型的共同类型成员因此它们不是Shape联合类型的类型成员。示例如下typeShapeCircle|Rectangle;declareconsts:Shape;s.radius;// 错误s.width;// 错误s.height;// 错误对于联合类型的属性签名其类型为所有成员类型中该属性类型的联合类型。例如下例中联合类型“Circle | Rectangle”具有属性签名area其类型为Circle类型中area属性的类型和Rectangle类型中area属性的类型组成的联合类型即“bigint | number”类型。示例如下interfaceCircle{area:bigint;}interfaceRectangle{area:number;}declareconsts:Circle|Rectangle;s.area;// bigint | number如果联合类型的属性签名在某个成员类型中是可选属性签名那么该属性签名在联合类型中也是可选属性签名否则该属性签名在联合类型中是必选属性签名。示例如下interfaceCircle{area:bigint;}interfaceRectangle{area?:number;}declareconsts:Circle|Rectangle;s.area;// bigint | number | undefined此例中area属性在Rectangle类型中是可选的属性。因此在联合类型“Circle | Rectangle”中area属性也是可选属性。2.2.2、索引签名索引签名包含两种即字符串索引签名和数值索引签名。在联合类型中这两种索引签名具有相似的行为。如果联合类型中每个成员都包含字符串索引签名那么该联合类型也拥有了字符串索引签名字符串索引签名中的索引值类型为每个成员类型中索引值类型的联合类型否则该联合类型没有字符串索引签名。例如下例中的联合类型T相当于接口类型T0T1interfaceT0{[prop:string]:number;}interfaceT1{[prop:string]:bigint;}typeTT0|T1;interfaceT0T1{[prop:string]:number|bigint;}如果联合类型中每个成员都包含数值索引签名那么该联合类型也拥有了数值索引签名数值索引签名中的索引值类型为每个成员类型中索引值类型的联合类型否则该联合类型没有数值索引签名。例如下例中的联合类型T相当于接口类型T0T1interfaceT0{[prop:number]:number;}interfaceT1{[prop:number]:bigint;}typeTT0|T1;interfaceT0T1{[prop:number]:number|bigint;}2.2.3、调用签名与构造签名如果联合类型中每个成员类型都包含相同参数列表的调用签名那么联合类型也拥有了该调用签名其返回值类型为每个成员类型中调用签名返回值类型的联合类型否则该联合类型没有调用签名。例如下例中的联合类型T相当于接口类型T0T1interfaceT0{(name:string):number;}interfaceT1{(name:string):bigint;}typeTT0|T1;interfaceT0T1{(name:string):number|bigint;}同理如果联合类型中每个成员都包含相同参数列表的构造签名那么该联合类型也拥有了构造签名其返回值类型为每个成员类型中构造签名返回值类型的联合类型否则该联合类型没有构造签名。例如下例中的联合类型T相当于接口类型T0T1interfaceT0{new(name:string):Date;}interfaceT1{new(name:string):Error;}typeTT0|T1;interfaceT0T1{new(name:string):Date|Error;}3、交叉类型交叉类型在逻辑上与联合类型是互补的。联合类型表示一个值的类型为多种类型之一而交叉类型则表示一个值同时属于多种类型。交叉类型通过交叉类型字面量来定义。3.1、交叉类型字面量交叉类型由两个或多个成员类型构成各成员类型之间使用“”符号分隔。示例如下interfaceClickable{click():void;}interfaceFocusable{focus():void;}typeTClickableFocusable;此例中定义了一个名为T的交叉类型。该交叉类型由两个成员类型组成即Clickable类型和Focusable类型。若一个值既是Clickable类型又是Focusable类型那么我们说该值的类型为交叉类型“Clickable Focusable”。它表示既可以点击又可以获得焦点的对象。3.1.1、成员类型的运算与联合类型相似如果交叉类型中存在多个相同的成员类型那么相同的成员类型将被合并为单一成员类型。示例如下typeT0boolean;typeT1booleanboolean;typeT2booleanbooleanboolean;此例中T0、T1和T2都表示同一种类型boolean。交叉类型是有序的成员类型的集合。在绝大部分情况下成员类型满足类似于数学中的“加法交换律”即改变成员类型的顺序不影响交叉类型的结果类型。例如下例中虽然交叉类型T0与T1中成员类型的定义顺序不同但是T0与T1表示相同的类型interfaceClickable{click():void;}interfaceFocusable{focus():void;}typeT0ClickableFocusable;typeT1FocusableClickable;需要注意的是当交叉类型涉及调用签名重载或构造签名重载时便失去了“加法交换律”的性质。因为交叉类型中成员类型的顺序将决定重载签名的顺序进而将影响重载签名的解析顺序。示例如下interfaceClickable{register(x:any):void;}interfaceFocusable{register(x:string):boolean;}typeClickableAndFocusableClickableFocusable;typeFocusableAndFocusableFocusableClickable;functionfoo(clickFocus:ClickableAndFocusable,focusClick:FocusableAndFocusable){leta:voidclickFocus.register(foo);letb:booleanfocusClick.register(foo);}此例第8行和第9行使用不同的成员类型顺序定义了两个交叉类型。第15行调用“register()”方法的返回值类型为void说明在ClickableAndFocusable类型中Clickable接口中定义的“register()”方法具有更高的优先级。第16行调用“register()”方法的返回值类型为boolean说明FocusableAndFocusable类型中Focusable接口中定义的“register()”方法具有更高的优先级。此例也说明了调用签名重载的顺序与交叉类型中成员类型的定义顺序是一致的。交叉类型中的类型成员同样满足类似于数学中的“加法结合律”。对部分类型成员使用分组运算符不影响交叉类型的结果类型。例如下例中的T0和T1类型是同一种类型interfaceClickable{click():void;}interfaceFocusable{focus():void;}interfaceScrollable{scroll():void;}typeT0(ClickableFocusable)Scrollable;typeT1Clickable(FocusableScrollable);3.1.2、原生类型交叉类型通常与对象类型一起使用。虽然在交叉类型中也允许使用原始类型成员但结果类型将成为never类型因此在实际代码中并不常见。示例如下typeTbooleannumberstring;此例中类型T是boolean、number和string类型组成的交叉类型。根据交叉类型的定义若一个值是T类型那么该值既是boolean类型又是number类型还是string类型。显然不存在这样一个值所以T类型为never类型。never类型是尾端类型是一种不存在可能值的类型。3.2、交叉类型的类型成员3.2.1、属性签名只要交叉类型I中任意一个成员类型包含了属性签名M那么交叉类型I也包含属性签名M。例如有以下的接口类型A和BinterfaceA{a:boolean;}interfaceB{b:string;}那么接口类型A和B的交叉类型“A B”为如下对象类型{a:boolean;b:string;}对于交叉类型的属性签名其类型为所有成员类型中该属性类型的交叉类型。例如有以下接口类型A和BinterfaceA{x:{a:boolean};}interfaceB{x:{b:boolean};}那么接口类型A和B的交叉类型“A B”为如下对象类型{x:{a:boolean}{b:boolean}}该类型也等同于如下类型{x:{a:boolean;b:boolean;};}若交叉类型的属性签名M在所有成员类型中都是可选属性那么该属性签名在交叉类型中也是可选属性。否则属性签名M是一个必选属性。例如有以下接口类型A和BinterfaceA{x:boolean;y?:string;}interfaceB{x?:boolean;y?:string;}那么接口类型A和B的交叉类型“A B”为如下对象类型{x:boolean;y?:string;}在“A B”交叉类型中属性x是必选属性属性y是可选属性。3.2.2、索引签名如果交叉类型中任何一个成员类型包含了索引签名那么该交叉类型也拥有了索引签名否则该交叉类型没有索引签名。例如有以下接口类型A和BinterfaceA{[prop:string]:string;}interfaceB{[prop:number]:string;}那么接口类型A和B的交叉类型“A B”为如下对象类型它同时包含了字符串索引签名和数值索引签名{[prop:string]:string;[prop:number]:string;}交叉类型索引签名中的索引值类型为每个成员类型中索引值类型的交叉类型。例如有以下接口类型A和BinterfaceA{[prop:string]:{a:boolean};}interfaceB{[prop:string]:{b:boolean};}那么接口类型A和B的交叉类型“A B”为如下对象类型{[prop:string]:{a:boolean}{b:boolean};}该类型也等同于如下类型{[prop:string]:{a:boolean;b:boolean;};}3.2.3、调用签名与构造签名若交叉类型的成员类型中含有调用签名或构造签名那么这些调用签名和构造签名将以成员类型的先后顺序合并到交叉类型中。例如有以下接口类型A和BinterfaceA{(x:number):number;}interfaceB{(x:string):string;}那么交叉类型“A B”为如下对象类型{(x:boolean):boolean;(x:string):string;}同时交叉类型“B A”为如下对象类型{(x:string):string;(x:boolean):boolean;}通过这两个例子能够看到交叉类型中调用签名的顺序与交叉类型类型成员的顺序相同构造签名同理。当交叉类型中存在重载签名时需要特别留意类型成员的定义顺序。3.3、交叉类型与联合类型3.3.1、优先级当表示交叉类型的“”符号与表示联合类型的“|”符号同时使用时“”符号具有更高的优先级。“”符号如同数学中的乘法符号“×”而“|”符号则如同数学中的加法符号“”。例如有如下复合类型AB|CD该类型等同于如下类型(AB)|(CD)还要注意当表示交叉类型的“”符号与表示联合类型的“|”符号与函数类型字面量同时使用时“”符号和“|”符号拥有更高的优先级。例如有如下类型()bigint|number该类型等同于如下类型即返回值类型为联合类型“bigint |number”的函数类型()(bigint|number)而不是函数类型和number类型的联合类型如下所示(()bigint)|number在任何时候我们都可以使用分组运算符“()”来明确指定优先级。3.3.2、分配率性质由交叉类型和联合类型组成的类型满足类似于数学中乘法分配律的规则。表示交叉类型的“”符号如同数学中的乘法符号“×”而表示联合类型的“|”符号则如同数学中的加法符号“”。下例中的“≡”符号是恒等号表示符号两侧是恒等关系A(B|C)≡(AB)|(AC)另一个稍微复杂的示例如下所示(A|B)(C|D)≡AC|AD|BC|BD如果我们对照着数学中的“乘法分配律”那么此例中的结果将不难理解。了解了交叉类型与联合类型的分配律性质后我们就能够分析与理解一些复杂的类型。例如有如下的复合类型T(string|0)(number|a);利用上文介绍的规则将该类型展开就能得到最终的结果类型如下所示T(string|0)(number|a);(stringnumber)|(stringa)|(0number)|(0a);// 没有交集的原始类型的交叉类型是 never 类型never|a|0|never;// never 尾端类型是所有类型的子类型// 并且若某成员是其他成员的子类型则可以从联合类型中消去a|0;