• 聊聊 TypeScript 中的类型保护


    聊聊 TypeScript 中的类型保护

    在 TypeScript 中使用联合类型时,往往会碰到这种尴尬的情况:

    interface Bird {
      	// 独有方法
        fly();
      	// 共有方法
        layEggs();
    }
    
    interface Fish {
      	// 独有方法
        swim();
      	// 共有方法
        layEggs();
    }
    
    function getSmallPet(): Fish | Bird {
        // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // 正常
    pet.swim();    // ts 报错
    

    如上所示,getSmallPet 函数中,既可以返回 Fish 类型的对象,又可以返回 Bird 类型的对象。由于返回的对象类型不确定,所以使用联合类型对象共有的方法时,一切正常,但是使用联合类型对象各自独有的方法时,ts 会报错。

    那么如何解决这个问题呢?最粗暴的方法当然是将联合类型转换为 any,不过这种方法不值得提倡,毕竟我们写的是 TypeScript 而不是 AnyScript。

    此时,我们使用今天的主角——类型保护,闪亮登场,它可以完美的解决这个问题。

    孔乙己说过,茴香豆有四种写法,同理,实现类型保护,也有四种写法。

    类型断言

    类型断言是最常用的一种类型保护方法,即直接指定类型。由于,TypeScript 中识别的类型大多是靠 TypeScript 的自动类型推算算出来的,所以会出现上面所说的那种问题,即 TypeScript 不知道具体对象类型是什么,所以不确定有没有联合类型各自独有的方法。

    当使用类型断言直接指定类型时,相当于你让 TypeScript 开启了上帝模式,可以直接知道具体类型是联合类型中的那个,此时再使用对象的独有方法就符合 TypeScript 的推断了。

    interface Bird {
      // 独有方法
      fly();
      // 共有方法
      layEggs();
    }
    
    interface Fish {
      // 独有方法
      swim();
      // 共有方法
      layEggs();
    }
    
    function getSmallPet(): Fish | Bird {
      // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // 正常
    // 通过鸭子类型来进行判断
    if ((pet as Bird).fly) {
      // 类型断言
      (pet as Bird).fly()
    } else {
      // 类型断言
      (pet as Fish).swim()
    }
    

    如果嫌弃通过 as 来进行类型断言不够上流,还可以使用类泛型的写法,即:

    let pet = getSmallPet();
    pet.layEggs(); // 正常
    // 通过鸭子类型来进行判断
    if ((<Bird>pet).fly) {
      (<Bird>pet).fly()
    } else {
      (<Fish>pet).swim()
    }
    

    tips:友情提示,虽然使用类泛型写法进行类型断言看起来高端一些,但是由于在 tsx 中语法存在歧义,所以为了统一起见,推荐使用 as 的方法进行类型断言。

    in 语法

    在 JS 中,我们经常使用 in 语法来判断指定的属性是否在指定的对象或其原型链中。

    同理,在 TypeScript 中,我们可以通过这种方法确认对象类型。

    interface Bird {
      // 独有方法
      fly();
      // 共有方法
      layEggs();
    }
    
    interface Fish {
      // 独有方法
      swim();
      // 共有方法
      layEggs();
    }
    
    function getSmallPet(): Fish | Bird {
      // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // 正常
    // 使用 in 语法进行类型保护
    if ('fly' in pet) {
      pet.fly()
    } else {
      pet.swim()
    }
    

    原理同类型断言一样,都是引导 TypeScript 的类型推断,确定对象类型。

    instanceof 语法

    当联合类型中使用的是 class 而不是 interface 时,instanceof 语法就派上用场了,通过 instanceof 语法可以区分不同的 class 类型。

    class Bird {
      // 独有方法
      fly() {};
      // 共有方法
      layEggs() {};
    }
    
    class Fish {
      // 独有方法
      swim() {};
      // 共有方法
      layEggs() {};
    }
    
    function getSmallPet(): Fish | Bird {
      // ...
    }
    
    let pet = getSmallPet();
    pet.layEggs(); // 正常
    // 使用 in 语法进行
    if (pet instanceof Bird) {
      pet.fly()
    } else {
      pet.swim()
    }
    

    typeof 语法

    typeof 语法不同于 in 语法以及 instanceof 语法,in 语法以及 instanceof 语法都是用来引导类型推断进行不同对象类型推断,而 typeof 语法常用于基本类型的推断(或者是联合使用基本类型和对象类型)。

    简而言之,当使用 typeof 能够区分联合类型中的不同类型时,即可使用它。

    function getSmallPet(): number | string {
      // ...
    }
    
    let pet = getSmallPet();
    if (typeof pet === 'number') {
      pet++
    } else {
      pet = Number(pet) + 1
    }
    

    总结

    就如茴香豆的四种写法的本质依然是茴香豆一样,类型保护的四种写法的本质也是一样的,即,引导 TypeScript 中的类型推断将类型推断的多选题变为单选题,这就是类型保护的本质。

  • 相关阅读:
    C
    A
    L
    G
    关于html()、val()、text()
    EL表达式
    JSON书写格式示例
    Servlet获取项目名的方法
    修改完Servlet后不用重启项目的设置方法
    浮动
  • 原文地址:https://www.cnblogs.com/karthuslorin/p/12834076.html
Copyright © 2020-2023  润新知