hackftz

bits change world

导航

深入理解TypeScript——文档篇之枚举

Posted on 2020-10-13 22:29  hackftz  阅读(76)  评论(0编辑  收藏  举报

一、概念

使用枚举我们可以定义一些带名字的常量。
我理解的是使用枚举,可以解决我们在项目中定义常量不规范的问题。

  1. 数字枚举

    // 使用初始化器
    enum Direction { // 定义数字枚举
        Up = 1, // 使用初始化器,初始值1
        Down, // 2
        Left, // 3
        Right // 4
        // ...定义依次递增
    }
    
    // 不使用初始化器
    enum Direction {
        Up, // 0
        Down, // 1
        Left,
        Right,
    }
    

    通过枚举的属性来访问枚举成员,和枚举的名字来访问枚举类型

    enum Response {
        No = 0,
        Yes = 1,
    }
    
    function respond(recipient: string, message: Response): void {
        console.log(recipient + ' ' +  message);
    }
    
    respond("Princess Caroline", Response.Yes) // Princess Caroline 1
    respond("Princess Caroline", 1000) // Princess Caroline 1000 这里值没有在Response里定义也不报错
    respond("Princess Caroline", 'df') // error 类型“"df"”的参数不能赋给类型“Response”的参数。ts(2345)
    

    数字枚举中使用函数计算得出的常量成员,后面紧跟着的常量成员必须使用常量进行初始化。

    function getSomeValue(): number {
      return 123
    }
    
    enum E {
      A = getSomeValue(),
      B = 1, // error! 前面的'A'不是常量初始化的,所以'B'需要一个初始化器
      C // 2 no error
    }
    
  2. 字符串枚举

    在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。

    enum Direction {
        Up = "UP",
        Down = "DOWN",
        Left = "LEFT",
        Right = "RIGHT",
    }
    
  3. 异构枚举

    enum BooleanLikeHeterogeneousEnum { // 不建议这样使用
        No = 0,
        Yes = "YES",
    }
    
  4. 计算的成员和常量成员

  • 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0

    // E.X is constant:
    enum E { 
        X // 0
    }
    
  • 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1。

    enum E2 {
        A = 1, 
        B, // 2
        C // 3
    }
    
  • 枚举成员使用 常量枚举表达式初始化。
    使用规则:
    一个枚举表达式字面量(主要是字符串字面量或数字字面量)
    一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
    带括号的常量枚举表达式
    一元运算符 +, -, ~其中之一应用在了常量枚举表达式
    常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象
    若常数枚举表达式求值后为 NaN或 Infinity,则会在编译阶段报错。

    enum FileAccess {
        // constant members
        None,
        Read    = 1 << 1,
        Write   = 1 << 2,
        ReadWrite  = Read | Write,
        // computed member
        G = "123".length
    }
    
  1. 枚举成员成为类型

    enum ShapeKind {
      Circle,
      Square,
    }
    
    interface Circle {
      kind: ShapeKind.Circle;
      radius: number;
    }
    
    interface Square {
      kind: ShapeKind.Square;
      sideLength: number;
    }
    
    let c: Circle = {
      kind: ShapeKind.Square, // Error!
      // kind: 1, no error 
      // kind: 123, no error
      // kind: 'str', // 不能将类型“string”分配给类型“ShapeKind.Circle”。
      radius: 100,
    }
    

    短路问题

    enum E {
        Foo,
        Bar
    }
    
    function f(x: E) {
        if (x !== E.Foo || x !== E.Bar) { // 判断无意义,总是会通过第一个判断
            //             ~~~~~~~~~~~
            // Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
            console.log(x);
        }
    }
    
  2. 将枚举当作参数传入函数中

    枚举对象

    enum E {
      X, Y, Z
    }
    
    function f(obj: { X: number }) {
      return obj.X; // Works, since 'E' has a property named 'X' which is a number.
    }
    
    console.log(f(E)); // 0
    

    反向映射:从枚举值到枚举名字。

    enum Enum {
        A
    }
    let a = Enum.A;
    let nameOfA = Enum[a]; // "A"
    

    生成的代码中,枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。

    要注意的是 不会为字符串枚举成员生成反向映射。

  3. const枚举
    减少重复代码定义产生的代码量开销
    避免非直接的对枚举成员的访问

    const enum Enum {
        A = 1,
        B = A * 2
    }
    

    常量枚举只能使用常量枚举表达式,并且不同于常规的枚举,它们在编译阶段会被删除。
    常量枚举成员在使用的地方会被内联进来。
    常量枚举不允许包含计算成员。

    // 使用常量枚举
    const enum Directions {
        Up,
        Down,
        Left,
        Right
    }
    
    let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right] // [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]
    
    // 使用计算初始化枚举成员
    function getval() {
      return 123
    }
    
    enum Directions {
        Up = getval(),
        Down = 1
    }
    
    let directions = [Directions.Up, Directions.Down] // [123, 1]
    
  4. 外部枚举

    外部枚举用来描述已经存在的枚举类型的形状。

    declare enum Enum { // 没有完全弄懂这个declare,据网上博文描述,
        A = 1,
        B,
        C = 2
    }
    

    外部枚举和非外部枚举之间有一个重要的区别,在正常的枚举里,没有初始化方法的成员被当成常数成员。 对于非常数的外部枚举而言,没有初始化方法时被当做需要经过计算的。