一、概念

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

  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
}
  1. 字符串枚举
    在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
enum Direction {
    Up = "UP",
    Down = "DOWN",
    Left = "LEFT",
    Right = "RIGHT",
}
  1. 异构枚举
enum BooleanLikeHeterogeneousEnum { // 不建议这样使用
    No = 0,
    Yes = "YES",
}
  1. 计算的成员和常量成员
  • 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 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);
    }
}
  1. 将枚举当作参数传入函数中
    枚举对象
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)。 引用枚举成员总会生成为对属性访问并且永远也不会内联代码。

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

  1. 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]
  1. 外部枚举
    外部枚举用来描述已经存在的枚举类型的形状。
declare enum Enum { // 没有完全弄懂这个declare,据网上博文描述,
    A = 1,
    B,
    C = 2
}

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