TypeScript | 枚举 Enum

554 阅读3分钟

使用枚举我们可以定义一些带名字的常量。TypeScript 支持数字的和基于字符串的枚举。

数字枚举

声明一个枚举类型,如果没有赋值,它们的值默认为数字类型且从 0 开始累加:

enum Months {
  Jan,
  Feb,
  Mar,
  Apr
}
Months.Jan === 0 // true
Months.Feb === 1 // true
Months.Mar === 2 // true
Months.Apr === 3 // true

// 如果从第一个数字赋值,往后值依次累加
enum Months {
  Jan = 1,
  Feb,
  Mar,
  Apr
}

Months.Jan === 1 // true
Months.Feb === 2 // true
Months.Mar === 3 // true
Months.Apr === 4 // true

字符串枚举

enum TokenType {
  ACCESS = 'accessToken',
  REFRESH = 'refreshToken'
}

// 两种不同的取值写法
console.log(TokenType.ACCESS === 'accessToken')        // true
console.log(TokenType['REFRESH'] === 'refreshToken')   // true

异构枚举

数字类型和字符串类型可以混合使用,但是不建议

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

计算的和常量成员

每个枚举成员都带有一个值,它可以是常量或计算出来的。

当满足如下条件时,枚举成员被当作是常量:

  1. 枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0:
enum E { X } // E.X is constant
  1. 枚举成员不带初始化器,但是它前一个枚举成员使用了常数来初始化:
//All enum members in 'E1' and 'E2' are constant.
enum E1 { X, Y, Z }
enum E2 {
    A = 1, B, C
}
  1. 枚举成员使用常量枚举表达式初始化。

常量枚举表达式是TypeScript表达式的子集,它可以在编译阶段求值。 当一个表达式满足下面条件之一时,它就是一个常量枚举表达式:

  • 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
enum E1 { one = 10, two, three }
enum E2 { one = 'one', two='two', three='three'}
  • 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
// one 是对E2.one的引用,two 是对当前枚举成员one的引用
enum E3 { one = E2.one, two = 2 * one }
  • 带括号的常量枚举表达式
//即可以是函数的调用,也可以是计算表达式的求值
function returnNumber (x: number): number {
  return x
}
enum E4 {
  one = (function () { return 1 })(),
  two = returnNumber(10),
  three = (E1.one + E1.two) % E1.three
}
  • 一元运算符 +, -, ~其中之一应用在了常量枚举表达式
enum E5 {
  one = ~E1.one,// 取反运算
  two = +E1.two,
  three = -E1.three,
  four = void 0
}
  • 常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。
enum E6 {
  // 左移运算
  one = 2 << 5,
  // 右移运算
  two = 64 >> 5,
  three = 64 >>> 5,
  // 或 运算合并 乘性运算
  four = (one | two) * three
}

所有其它情况的枚举成员被当作是需要计算得出的值:

enum FileAccess {
    // constant members
    None,
    Read    = 1 << 1,
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // computed member
    G = "123".length
}

反向映射

所谓的反向映射就是指枚举的取值,不但可以正向的 Months.Jan 这样取值,也可以反向的 Months[1] 这样取值。

enum Months {
  Jan = 1,
  Feb,
  Mar,
  Apr
}
console.log(Months.Mar === 3) // true
console.log(Months[3] === 'Mar')  // true

编译后的 JavaScript 代码:

'use strict'
var Months;
(function (Months) {
  Months[Months['Jan'] = 1] = 'Jan'
  Months[Months['Feb'] = 2] = 'Feb'
  Months[Months['Mar'] = 3] = 'Mar'
  Months[Months['Apr'] = 4] = 'Apr'
})(Months || (Months = {}))

Tips:

  • 字符串枚举成员不会生成反向映射。
  • 枚举类型被编译成一个对象,它包含了正向映射( name -> value)和反向映射( value -> name)。

const 枚举

在枚举上使用 const 修饰符:

enum Months {
  Jan = 1,
  Feb,
  Mar,
  Apr
}

const month = Months.Mar

编译后的内容:

'use strict'
const month = 3 /* Mar */

枚举类型应该编译出的对象没有了,只剩下 month 常量。这就是使用 const 关键字声明枚举的作用。因为变量 month 已经使用过枚举类型,在编译阶段 TypeScript 就将枚举类型抹去,这也是性能提升的一种方案。

枚举合并

分开声明名称相同的枚举类型,会自动合并:

enum Months {
  Jan = 1,
  Feb,
  Mar,
  Apr
}
enum Months {
  May = 5,
  Jun
}
console.log(Months.Apr) // 4
console.log(Months.Jun) // 6

学习链接