前言: 基础类型,字面量类型,函数类型以及接口类型这些都是单一的,原子的类型元素。 实际编程中, 通常我们要处理的都是复杂的场景,我们可以通过组合,交叉这些单一地、原子类型来构造更复杂的类型。这种组合和交叉在TypeScript中有专业的术语:联合类型(Union Types),交叉类型(Intersection Types)
联合类型
所谓联合类型,即表示变量,参数的类型不是单一原子类型,而可能是多种不同的类型的组合, 通过|表示。
function unionTypes(size: number | string) {}
【注意】: 使用联合类型之前,我们可以会使用any或unkown类型来表示参数的类型(推荐使用unknown。)。
使用类型别名定义联合类型
type ModernUnit = 'vh' | 'vw'
type Unit = 'px' | 'em' | 'rem'
type MessedUp = NodernUnit | Unit; // 将ModernUnit和Unit联合起来
将string类型与string字面量类型组合成一个联合类型,会是什么效果呢?
答案是类型为缩减为string的类型,即基本类型。
使用接口类型组成联合类型
interface Bird {
fly(): void;
layEggs() : void;
}
interface Fish {
swim(): void;
layEggs(): void;
}
const getPet: () => Bird | Fish = () => {
return {} as Bird | Fish
}
const pet = getPet()
pet.layEggs(); //ok
pet.fly();// ts2339
如果需要使用fly方法,需要使用类型守卫来收缩类型至Bird类型
需要注意的是, 直接使用pet.fly时,就会提示ts2339(Fish没有fly属性, Fish | Bird没有fly属性), 可以使用in,as类型守卫
// ts 2339
if(pet.fly){}
if(typeof pet.fly === 'function'){}
// ok
if('fly' in pet){}
(pet as Bird).fly()
交叉类型
所谓交叉类型(intersection type),它可以把多个类型合并成一个类型,合并后的类型将拥有所有成员类型的特性。使用 &表示。
// 合并原始类型没有意义, 因为没有满足同时是string和number的类型
type Useless = string & number;
合并接口类型
将多个接口合并成一个类型,从而实现同等接口继承的效果。
type IntersectionType = {id: number, name: string;} & {age: number;}
问题,如果和宾得的多个接口类型存在同名属性会是什么效果?
分两种情况:
- 两个类型不兼容。 比如number与string交叉后是never类型,不能给这个属性赋值了(ts2322)
- 两个类型兼容。 比如number和number的子类型2, 合并后为子类型
type intersectionTypeConflict = {name: 2} & {name: number} // 交叉后 {name: 2}
合并联合类型
多个联合类型交叉时,结果是提取所有联合类型的相同类型成员, 即多个联合类型交叉时是求交集。如何没有相同的类型成员(交集为空),则交叉后的类型为never
type UnionA = 'px' | 'em' | 'rem' | '%';
type UnionB = 'vh' | 'vw' | 'rem' | 'pt';
type IntersectionUnion = Union & Union; // rem
type UnionC = 'wpx';
type IntersectionUnionB = UnionA & UnionC; // never
联合,交叉组合
当联合,交叉类型组合时,联合操作符|优先级低于交叉操作符&(与javascript的逻辑或||,逻辑与&&表现一致)。可以使用括号()调整优先级。
type IntersectionA = {id: number} & {name: string} | {id: string} & {name: number}
// 等价于{id:number,name: string} | {id: string, name: number}
type UnionIntersectionB = ('px'| 'em'| 'rem'| '%') & ('vh'| 'vw'| 'em')
类型缩减
如果将基础类型,基础类型的子类型联合,得到的类型为基础类型。 枚举也联合成枚举基础类型。
type URStr = 'str' | string; // string 类型
type URNum = 12 | number; // number类型
type URBoolen = true | boolean; // boolean类型
type UREnum = {
ONE,
TWO
}
type URE = UREnum.ONE | UREnum; // UREnum类型
缩减掉字面量类型,枚举成员类型,只保留原始类型,枚举类型等父类型。 这是合理的。
但是如: type BorderColor = 'black' | 'red' | 'yelllow' | 'green' | string, 类型联合后,BorderColor联合成string基础类型,VS Code就不能给出前面定义好的类型的提示。 不过ts提供了 黑魔法--只需给父类型添加&{} 即可。
联合类型的成员是接口
如果满足其中一个接口的属性时另外一个接口属性的子类型, 这个属性也会类型缩减。
type UnionIntersection =
{
age: '1'
}
|
{
age: '1' | '2',
[key: string]: string;
}
因为'1'是'1' | '2'的子类型, 所以类型缩减为'1' | '2'
应用: {age: 1, otherProperty: string},想要指定age为number类型,可以和一个number的子类型进行联合。
type UnionInterce =
{
age: number
}
|
{
age: never;
[key: string]: string
}
因为never是所有类型的子类型。所以number和never缩减后后为number类型。
The End!
传送门:
总结: any 既是所有类型的父类型, 也是所有类型的子类型 unknown 是所有类型的父类型 void 无任何类型 never 是所有类型的子类型