TypeScript 泛型中的交叉和联合类型

783 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

泛型其实也相当于一种小语言,它有自己的一套语法类型,如局部类型、联合类型、交叉类型、索引类型、映射对象类型、条件类型,这写语法类型可以让我们可以自由的创建我们的泛型。

局部类型

和我们说的局部变量一样,创建的类型它也是有自己的作用域。比如我们在函数中、在if语句中等{}中声明的类型就是会在当前的作用域里面使用,类似域let const定义的变量一样。

function getName(){
  interface Person<T> {
    name: T;
  }
  
  type State<U> = Person<U>;
}

type Status<T> = Person<T>; // 错误 当前作用域里面没有Person泛型接口

联合类型

联合类型表示一个值的类型可以为若干种类型之一,比如一个数值1的类型是数值类型和字符串类型中的一个,那个我们将数值类型和字符串类型联合起来,就叫做联合类型, 它用符号“|”分隔。

type Age = string | number;
const age: Age = 1;

但是我们将1赋值给age(Age类型)后,我们能否调用string类型的split方法呢,显然是不可以的,不然程序就会出错,那我们能否调用toString方法呢,显然是可以的,程序也不会报错,因为age的值肯定是它们两者之间的类型,number和string类型都有toString方法。因此这里我们能看到联合类型会将组成它的类型下的所有类型成员求了一个交集获取它们共同的类型成员,这写共同的成员就是联合类型的类型成员。

interface IPhone {
  width: number;
  height: string;
  isIos: boolean;
}
interface XiaoMi {
  width: number;
  height?: number;
  isAndroid: boolean;
}
// 联合类型
type Phone = IPhone | XiaoMi;
// Phone和此接口其实是相似的,联合类型就是只保留同名的属性,类型进行兼容,
// 不同的是两者类型之间赋值是不一样的,Phone类型的赋值是必须满足是它子类型中的一个,但是使用的时候只能用和如下接口中的一样的成员。
inferface _Phone = {
  width: number;
  height?: number| string;
}

交叉类型

和联合类型语法差不多,都是多个类型进行组合,只是用“&”符号隔开。但是语义和和联合类型刚好相反,表示一个值的类型可以是若干种类型,注意并不是之一, 以联合类型中的第一个例子为例:

type Age = string & number;

表示Age即使string类型也是number类型,哪里有可以这样的类型,当然没有那就是never类型。举个正常的例子:

interface IPhone {
  width: number;
  height: string;
  isIos: boolean;
}
interface XiaoMi {
  width: number;
  height: string;
  isAndroid: boolean;
}
type Phone = IPhone & XiaoMi;
// dd 目前就是既可以赋值给IPhone类型也可以赋值给XiaoMi类型。
const dd: Phone = {
  width: 1,
  height:'1',
  isIos: false,
  isAndroid: false,
};

那交叉类型的类型成员有哪些呢,如上面的例子的dd的属性最少也要包含IPhone和XiaoMi的所有成员,故交叉类型的类型成员是子类型中多有类型的合并集合刚好和联合类型相反, 那他的类型成员的类型是什么呢,相当于将同名的类型成员再做一个交叉类型。

交叉类型和联合类型一起使用

它们一起是使用是什么结果呢,比如如下,还是使用上一例中定义的类型XiaoMi和IPhone

type Phone = XiaoMi | XiaoMi & IPhone;

如上怎么计算Phone的类型呢,其实我们可以采用数学中的运算符号形式来算,TS中交叉符号优先级大于联合符号。我们就将它们分别比作乘号×和加号+,如下

// 伪代码
type Phone = XiaoMi + (XiaoMi * IPhone);

通过这种方式我们就能更好的计算出它们的类型类。

// 伪代码
  X = (XiaoMi & IPhone) = {
    width: number;
    height: string;
    isIos: boolean;
    isAndroid: boolean;
  }
  
  XiaoMi | XiaoMi & IPhone = XiaoMi | X = {
    width: number;
    height: string;
    isIos: boolean;
  }

当然,TS类型当中也可以有括号,括号优先级最高。

type Phone = (XiaoMi | XiaoMi) & IPhone;