交叉类型

86 阅读1分钟

交叉类型满足以下特征:

  • 唯一性: A & A 等价于 A
  • 满⾜交换律: A & B 等价于 B & A
  • 满⾜结合律: (A & B) & C 等价于 A & (B & C)
  • ⽗类型收敛:如果 B 是 A 的⽗类型,则 A & B将被收敛成 A 类型:
type A0 = number & 1; // 1 其中 number 是 1 的父类型
type A1 = string & "1"; // "1"
type A2 = boolean & true; // true

type A3 = any & 1 // any
type A4 = any & boolean // any
type A5 = any & never // never

另外,除了 never类型交叉运算结果为 never类型外,任何类型和 any 类型交叉运算,结果都是 any 类型。

在多个对象类型做交叉运算的时候,如果存在相同属性,且属性类型为非基本数据类型,则可以进行合并

type A1 = {
    name: string;
    age: number;
}

type A2 = {
    location: string;
}

type A3 = A1 & A2;
/*
type A3 = {
    name: string;
    age: number;
    location: string;
}
*/

若相同属性是可辨识的属性(即类型是字面量类型或者字面量类型组成的联合类型),则最后结果为 never类型。

type A = { kind: 'a', foo: string };
type B = { kind: 'b', foo: number };
type C = { kind: 'c', foo: number };

type AB = A & B; // never
type BC = B & C; // never

函数类型也支持交叉运算:

type F1 = (a: string, b: string) => void; 
type F2 = (a: number, b: number) => void;
var f: F1 & F2 = (a: string | number, b: string | number) => { }; 

f("hello", "world"); // Ok 
f(1, 2); // Ok
f(1, "test"); // Error

第三个报错是因为 TypeScript 编译器利用函数重载特性来实现不同函数类型的交叉运算。

可以再实现一个新的函数类型来解决该问题:

type F1 = (a: string, b: string) => void; 
type F2 = (a: number, b: number) => void;
type F3 = (a: number, b: string) => void;
var f: F1 & F2 & F3 = (a: string | number, b: string | number) => { }; 

f("hello", "world"); // Ok 
f(1, 2); // Ok
f(1, "test"); // Ok