1 minute read

用过别的开发语言的(比如 C#、Java 等)都应该接触过枚举值。在实际的开发中我们常常会遇到这样的情况。比如根据某条数据的状态来执行不同的操作,因此就会有如下的代码

if(status == 1){
    ....
}else if(status == 2){
    ....
}else{
    ....
}

但是这样的代码时很难知道数值 1、2 代表的是什么意思,别说不同的人来看了,就算同一个人,过段时间来看这个也不一定知道代表的意思。因此就有了枚举,使用枚举我们可以定义一些带名字的常量。 使用枚举可以清晰地表达意图或创建一组有区别的用例。

enum STATUS  {
    success = 1,
    error = 2,
    other = 3
}

接下来我们可以修改上面的代码

if(status == STATUS.success){
    ....
}else if(status == STATUS.error){
    ...
}else {
    ....
}

这样两者相比,下面的明显比上面的在语义上更加容易读懂。

接下来我们来看看 TypeScript 中的枚举。TypeScript 支持数字的和基于字符串的枚举。

数字枚举

上面我们定义的就是一个数字枚举,我们为每一个值都进行了初始化,其实我们也可以如下那么来定义

enum STATUS  {
    success = 1,
    error ,
    other
}

这样定义,那么后面的值会在第一个定义的值基础上自增。即error = 2,other = 3 ,如果第一个值定义为 2,那么后面的就是从 2 开始自增的,如果我们在中间某个值进行了初始化,那么该值后面的值会在这个值的基础上进行自增

enum STATUS {
    success,
    error = 5,
    other
}
//  success: 0,  error: 5,  other: 6

字符串枚举

顾名思义,就是枚举的值是字符串,而字符串就没有自增了,因此所有的枚举必须进行初始赋值

enum STATUS {
    success = 'success',
    error = 'error',
    other = 'other'
}

异构枚举

异构枚举就是同时使用了数字枚举和字符串枚举的

enum STATUS {
    success = 1,
    error = 'error',
    other = 'other'
}

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

// 它是枚举的第一个成员且没有初始化器,这种情况下它被赋予值 0
// E.X is constant:
enum E { X }

// 它不带有初始化器且它之前的枚举成员是一个 数字常量。 这种情况下,当前枚举成员的值为它上一个枚举成员的值加1
// All enum members in 'E1' and 'E2' are constant.
enum E1 { X, Y, Z }

enum E2 {
    A = 1, B, C
}

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

  • 一个枚举表达式字面量(主要是字符串字面量或数字字面量)
  • 一个对之前定义的常量枚举成员的引用(可以是在不同的枚举类型中定义的)
  • 带括号的常量枚举表达式
  • 一元运算符 +, -, ~其中之一应用在了常量枚举表达式
  • 常量枚举表达式做为二元运算符 +, -, *, /, %, <<, >>, >>>, &, |, ^的操作对象。 若常数枚举表达式求值后为 NaNInfinity,则会在编译阶段报错。
enum FileAccess {
    // constant members
    None,
    Read    = 1 << 1,  // 位运算
    Write   = 1 << 2,
    ReadWrite  = Read | Write,
    // computed member
    G = "123".length
}

反向映射

我们可以通过枚举的名字知道对应的值,也可以通过对应的值知道相应的名称,(这个在第一篇中已经有讲到了)

enum Color {red, green=3, blue}

编译后的

var Color
;(function (Color) {
    Color[(Color["red"] = 0)] = "red"
    Color[(Color["green"] = 3)] = "green"
    Color[(Color["blue"] = 4)] = "blue"
    console.log(Color) // { '0': 'red', '3': 'green', '4': 'blue', red: 0, green: 3, blue: 4 }
})(Color || (Color = {}))
var c = Color.green
console.log(c) // 3
console.log(Color[3]) // 'green'

const 枚举

常量枚举通过在枚举上使用 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]

生成后的代码为:

var directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */]

TypeScript–类型推论

简单的类型推论,比如下面

let x = 2

这样就会推论 x 是一个数字类型的。这种推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时。

当需要从几个表达式中推断类型时候,会使用这些表达式的类型来推断出一个最合适的通用类型。

let x = [0, 1, null]

为了推断x的类型,我们必须考虑所有元素的类型。 这里有两种选择: numbernull。 计算通用类型算法会考虑所有的候选类型,并给出一个兼容所有候选类型的类型。

当所有的候选类型共享相同的通用类型的时候,

// 没有使用通用类型
let zoo = [new Rhino(), new Elephant(), new Snake()] // (Rhino | Elephant | Snake)[]

// 使用了通用类型
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()] // Animal[]

Updated: