Typescript —— 枚举详解

481 阅读4分钟

枚举可以为定义一些带名字的常量,枚举可以清晰的表达意图,typescript支持数字枚举和字符串枚举,并且枚举是真是运行在编译后的代码中的。

数字枚举

enum sex{
  man,
  woman
}
sex.man; // 0
sex[0];   // 0
sex.woman; // 1
sex[1];  // 1 

数字枚举是自增的,并且默认值是从0开始的和相互反向映射的 正向映射(name->value

反向映射(value->name

以下是编译后的代码。

var sex;
(function (sex) {
    sex[sex["man"] = 0] = "man";
    sex[sex["woman"] = 1] = "woman";
})(sex || (sex = {}));

并且数值可以自定义,一旦遇到自定义数值,接下来的枚举属性都从自定义值开始自增,下面这个例子可以详细证明。

enum sex{
  man = 1, //自定义
  woman,
  boy = 5, //自定义
  girl
}
/************以下是编译后的代码********************/
var sex;
(function (sex) {
    sex[sex["man"] = 1] = "man";
    sex[sex["woman"] = 2] = "woman";
    sex[sex["boy"] = 5] = "boy";
    sex[sex["girl"] = 6] = "girl";
})(sex || (sex = {}));

字符串枚举

字符串枚举和数值有细微差别,没有自增,也没有反向映射,枚举成员必须用字符串字面量,或者另外一个字符串枚举成员进行初始化。换句话说,如果你在调试数值枚举运行时的值时,这个值的可读性不高,不能表达有用的信息(尽管反向代理有所帮助),字符串枚举允许你提供一个运行时有意义的值。

enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
/************以下是编译后的代码********************/
var Direction;
(function (Direction) {
    Direction["Up"] = "aa";
    Direction["Down"] = "DOWN";
    Direction["Left"] = "LEFT";
    Direction["Right"] = "RIGHT";
})(Direction || (Direction = {}));

异构枚举

一个枚举可以既有字符串成员和数值成员,但并不建议这样子去做,除非有必要的javascript特殊运行时的行为。

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES",
}

枚举成员值得类型

常量 和 计算后的,常量成员需要符合三个条件

  1. 它是枚举第一个成员并且无初始化器
  2. 它不带初始化器且前一个成员是一个数字常量,此时值是上一个枚举成员+1
  3. 枚举成员使用常量枚举初始化,常数枚举表达式可以在编译阶段求值,满足以下条件之一,就是常数表达式
    1. 一个字符串字面量或者数值字面量
    2. 一个对之前常量枚举成员得引用(可以来自不同枚举甚至本枚举)
    3. 一元、二元运算符运用在常量表达式中。如果计算后为NaN和undefined则编译时报错

不满足这些条件的都属于计算得出的枚举值。

enum FileAccess {
    // 常量成员
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // 不符合条件计算后得出的值
    G = "123".length
}

联合枚举

当枚举成员成为了接口中的成员的值,那么在赋值的时候可以得到一些有消息,例如:

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,类型'ShapeKind.Square' 不可以分配给 'ShapeKind.Circle
    radius: 100,
}

枚举成员本身使每个枚举成员的联合,类型系统可以知道枚举里的值,利用这个特点可以捕获比较愚蠢的错误。例如:

enum E {
    Foo,
    Bar,
}
function f(x: E) {
    if (x !== E.Foo || x !== E.Bar) {
       //这里枚举E只有两个成员,那么我们判断x !== E.Foo为true,if内逻辑执行,如果为false,那么x一定是 E.Bar,没必要继续检查是否为E.bar,所以语句后发生了短路。改为         
       }
}

运行时的枚举

枚举是运行时真正存在的对象,例如下面的例子

enum E {
    X, Y, Z
}
console.log(E.X) //输出 0

const枚举

当使用const修饰枚举时,会跟普通的枚举有区别:不存在编译后的代码中,常量枚举成员使用的地方会被直接替换成值。

const denum E {
    X = 1+1, Y, Z
}
console.log(E.X) //输出 0
/**************编译后代码*********************/
console.log(2 /* X */); //枚举相关已被删除替换

外部枚举

用来定义已经存在的枚举,使用declare来修饰枚举。外部枚举和非外部枚举之间有一个重要区别:在正常枚举里,没有初始化方法的成员被当作常数成员,而外部枚举则当作需要经过计算的。