TypeScript
中的交叉类型和联合类型是用来组合多个类型的方式。
&
符号将多个类型组合在一起,表示同时具备这些类型的特性。|
符号将多个类型组合在一起,表示可以是其中任意一个类型。交叉类型, 简单来说就是通过&
符号将多个类型进行合并成一个类型,然后用type来声明新生成的类型。
这里我举个例子,具体如下:
type A = { foo: number };
type B = { bar: string };
type C = A & B;
const obj: C = { foo: 123, bar: "abc" };
在上面的例子中,类型 C 是类型 A 和类型 B 的交叉类型,表示同时具备 foo 和 bar 属性。变量 obj 符合交叉类型 C 的定义,拥有 foo 和 bar 属性。这就是一个典型的交叉类型。
在使用交叉类型时,有几个注意点需要考虑:
问:任何类型都能通过 &
合并成新的类型吗?
答:这肯定是 不行 的,原子类型进行合并是没有任何意义,因为它们合并后的类型是 never
,比如 string & number
,这肯定是错误的,因为不可能有既满足字符串又能满足数字类型的值.
type A = string & number; // 错误:基本类型无法进行交叉操作
问:交叉的类型中具有同名属性,该怎么处理?
答:这里分两种情况,如果同名属性的类型相同则合并后还是原本类型,如果类型不同,则合并后类型为never
type A = { foo: string };
type B = { foo: string };
type C = A & B; // 合并后是never
const obj: C = { foo: "abc" }; // 使用类型断言解决冲突
type A = { foo: number };
type B = { foo: string };
type C = A & B; // 合并后是never
const obj: C = { foo: "abc" }; // 报错, 可以使用这个避免错误 { foo: "abc" as never };
注意:any 类型和除 never 类型以外的任何类型交叉时都为any
type A = any & 1; //any
type B = any & boolean; //any
type C = any & never; //never
let Aname: A = 'lining'
let Bname: B = 'lining'
其他情况比较:
type A = number & 1; //1
type B = 'maoxiansheng' & string; //'maoxiansheng'
type C = boolean & true; //true
interface A {
inner: D;
}
interface B {
inner: E;
}
interface C {
inner: F;
}
interface D {
d: boolean;
}
interface E {
e: string;
}
interface F {
f: number;
}
交叉类型使用
type ABC = A & B & C;
let abc: ABC = {
inner: {
d: false,
e: 'className',
f: 5
}
};
type A = {
kind:'a',
loyal:number
}
type B = {
kind:'b',
loyal:string
}
type AB = A&B;//never
type A = (a:number,b:number) => void
type B = (a:string,b:string) => void
type AB = A&B;
let func:AB = (a:number | string ,b:number | string) => {}
func(1,2)//正常
func('a','b')//正常
func(1,'b')//报错
type A = (a:number,b:number) => void
type B = (a:string,b:string) => void
type C = (a:number,b:string) => void
type ABC = A&B&C;
let func:ABC = (a:number | string ,b:number | string) => {}
func(1,2)//正常
func('a','b')//正常
func(1,'b')//正常
相信小伙伴能够看懂这里的逻辑了吧,就是交叉的类型需要成对的匹配,那假如再出现需要传递的参数是func(string,number)类型的参数,有应该如何处理?只需要再添加新的类型即可:type C = (a:string,b:number) => void
但是,实际操作可能不需要这么麻烦,除非必要必须这样做。通常我们会有更加简单的方案直接定义。
联合类型和交叉类型比较相似,联合类型通过 |
符号连接多个类型从而生成新的类型。
它主要是取多个类型的交集,即多个类型共有的类型才是联合类型最终的类型。联合类型可以是多个类型其中一个,可做选择,比如:string | number,它的取值可以是string类型也可以是number类型。
举几个例子,如下所示:
声明变量的时候设置变量类型
let a:string|number|boolean;
a = 's';
a = 1;
a= false;
多个接口类型进行联合
interface X{
q:number,
w:string,
r:string
}
interface Y{
q:number
r:string,
}
type XY = X | Y
let value:XY = {
q:1,
r:'r'
}
let value2:XY = {
q:1,
r:'r',
w: 'w'
}
错误演示,多余 x 属性。
interface X{
q:number,
w:string,
r:string
}
interface Y{
q:number
r:string,
}
type XY = X | Y
let value3:XY = {
q:1,
r:'r',
x: 'x' // Error,Type '{ q: number; r: string; x: string; }' is not assignable to type 'XY'.
}
函数接口类型进行联合
interface X{
x:()=> string;
y:()=> number;
}
interface Y{
x:()=>string;
}
type XY = X|Y;
function func1():XY{
//此处不进行类型断言为XY在编辑器中会报类型错误
return {} as XY
}
let testFunc = func1();
testFunc.x();
testFunc.y(); //Error:类型“XY”上不存在属性“y”,类型“Y”上不存在属性“y”。
另外我们还要注意,**testFunc.x()**还会报类型错误,我们需要用类型守卫来区分不同类型。这里我们用 in
操作符来判断
if('x' in testFunc) testFunc.x()
扩展:boolean 类型可以看成是 true | false 的联合类型
当字面量类型和原始类型进行联合,那么就会造成类型缩减。
type A = 'a' | string; //string类型
type B = false | boolean; //bolean 类型
type C = 1 | number; //number类型
如上,A是由字面量 a
和原始类型string组成,则会缩减为string类型。
enum Class{
A,
B
}
type C = Class.A | Class; //Class类型
注意⚠️:TS会把字面量类型和枚举成员类型给缩减掉,只剩下原始类型和枚举类型
当接口类型进行联合,接口中同名属性的类型不同,该怎么进行缩减呢?比如下面的例子
interface A{
name:string
}
interface B{
name:string | number
[property:string]:any
}
type AB = A|B
会缩减为B类型,可以实际查看该运行结果
interface A{
name:string
}
interface B{
name:string | number
[property:string]:any
}
type AB = A|B
let nameA: AB = { name: '' }
let nameB: AB = { name: 123 }
let nameC: AB = { name: 123, count: 256 }
以上就是TypeScript中交叉类型和联合类型的说明。感觉对自己有用的客观请不要吝啬你手中的三连,谢谢。