TypeScript快速入门教程(四)、联合类型 & 交叉类型 & 类型保护

876 阅读3分钟

文章快速索引:

1、TypeScript快速入门教程(一)、基础类型和变量声明
2、TypeScript快速入门教程(二)、面向对象知识(接口、类、抽象类)
3、TypeScript快速入门教程(三)、函数、范型使用
4、TypeScript快速入门教程(四)、联合类型 & 交叉类型 & 类型保护

联合类型交叉类型其实在生活中是一个很常见的案例。

  • 黄瓜,你说他是水果还是蔬菜呢?
  • 番茄是属于水果还是属于蔬菜呢?
  • 那么水果黄瓜呢?

其实我们单纯看黄瓜和番茄的话,其实既可以算是水果,也能算是蔬菜,其实就是场景决定的,我要做菜,它就是蔬菜,拿来生吃,就是水果呗。

基于以上的知识点,我们可以这样理解:

  • 联合类型, 它的类型既可以是 A,也可以是B,伪代码表示的话,就是 A | B
  • 交叉类型, 它的类型包含A的特点,也包含B的特点,伪代码表示的话,就是A & B

根据上面的分析,我们先定义出来一个水果和一个蔬菜的接口方便使用。

水果接口

interface Fruits{

    /**
     * 名称
     */
    name: string

    /**
     * 生吃
     */
    eatRawFood(): void
}

蔬菜接口

interface Vegetables{

    /**
     * 名称
     */
    name: string

    /**
     * 做菜
     */
    cooking() : void
}

1、联合类型

比如一个🍅,我既可以当水果,又可以当做蔬菜烹饪。这种场景下,就不能单独定义一个确定类型了。于是就可以用联合类型(A | B)定义:

function eatTomatoType(type: Fruits | Vegetables) {
	//方法体
}

是不是很完美了,你觉得没有问题?

那么问题又来了~ 😄,发现我开始三理三气的了。

比如我现在有一个🍅,我思考打算怎么吃?

function eatTomatoType(type: Fruits | Vegetables) {
    console.log(type.name);
   	type.cooking() //error
   	type.eatRawFood()  //error
}

假设,我现在还没有想好怎么吃?这个🍅的属性还没有定,我发现根本定不下来用什么方法食用~回到程序,就是发现type.cooking()type.eatRawFood()两个方法都不能正常调用。因为既可以是水果、又可以是蔬菜,让程序陷入了两难。

这个时候,就是该类型保护登场。类型保护最主要的作用就是帮我们确定出来一个合适的操作方式。类型保护是一种处理思维~

仔细看一下代码的代码

function eatTomatoType(type: Fruits | Vegetables) {
    console.log(type.name);
    //类型“Fruits | Vegetables”上不存在属性“cooking”。类型“Fruits”上不存在属性“cooking”。
    // type.cooking()   
    if('eatRawFood' in type){
        //如果type中存在eatRowFood方法,则表示是水果        
        (type as Fruits).eatRawFood()
    } else {
        (type as Vegetables).cooking
    }
}

这里使用了一个判断,'eatRawFood' in type是判断,当前type里面是否包含了eatRawFood方法。如果当前选择的方式是直接生吃,也就是我们之前定义好的水果类型咯。

class Tomato implements Fruits{
    
    name: string = '🍅';
    eatRawFood(): void {
        console.log(`${this.name} 是可以直接生吃的~`);
    }

}
eatTomatoType(new Tomato())

最终会打印出来

🍅
🍅 是可以直接生吃的~

2、交叉类型

我们还是用黄瓜举例,普通的黄瓜直接吃,口感可能就很一般,现在农科院为了让黄瓜能兼具生吃好吃和烹饪好吃的条件,研究了新品种,水果黄瓜登场了~

这个时候的水果黄瓜,如果类型表示的话,伪代码可能就是 黄瓜 & 水果了。

class SuperMarket implements Fruits, Vegetables{
    
    name: string = '🥒放进去了商超里面了';

    cooking(): void {
       console.log(`${this.name}, 可以买来烹饪!`);
    }
    
    eatRawFood(): void {
        console.log(`${this.name}, 可以买来生吃!`);
    }
}


let shoppingCart: Fruits & Vegetables = new SuperMarket();

shoppingCart.cooking()
shoppingCart.eatRawFood()

好了,现在新研制的水果黄瓜上市了,商超的蔬菜和水果柜台都上架了。当你买回去之后,既可以烹饪,又可以直接生吃。

当然这个例子有点牵强,理解问题就ok了。

3、 类型保护

其实类型保护在上面已经用过了,下面介绍一下常用的类型保护方式。

  • in 类型保护 in 用来判断是否归属于某个类中,上面有用到
'cooking' in Vegetables
  • typeof类型保护 typeof 是用来比较类型归属
cooking typeof Function
  • instanceof类型保护 instanceof 主要是用来比较对象
a instanceof A  //判断对象归属
  • 自定义类型保护 自定义类型保护,其实就是你自己定义的一个条件。
//比如你的条件如下
A && B //只有在满足了以下条件的时候,才是某个安全的类型

感觉各位大佬看到这里,此致~敬礼