重学TS --- enum

225 阅读4分钟

基本定义

这是我参与11月更文挑战的第23天,活动详情查看:2021最后一次更文挑战

枚举的主要作用是给可读性相对较差的常量赋予一组可读性较好的别名

在TS中支持字符串枚举数值枚举 两种枚举形式

如果没有赋初值的枚举默认就是数值枚举

// 枚举的基本定义
enum Direction {
  UP, // 0
  LEFT, // 1
  RIGHT, // 2 
  DOWN // 3
}
"use strict";
// 枚举的基本定义
var Direction;
(function (Direction) {
    Direction[Direction["UP"] = 0] = "UP";
    Direction[Direction["LEFT"] = 1] = "LEFT";
    Direction[Direction["RIGHT"] = 2] = "RIGHT";
    Direction[Direction["DOWN"] = 3] = "DOWN";
})(Direction || (Direction = {}));

所以默认情况下,枚举会在代码中生成一个在运行时候可以使用的对象,该对象名称即为枚举名

这是枚举和接口等其它TS特性最大的区别

// 上述枚举等价于
const Direction = {
  '0': 'UP',
  '1': 'LEFT',
  '2': 'RIGHT',
  '3': 'DOWN',
  UP: 0,
  LEFT: 1,
  RIGHT: 2,
  DOWN: 3
}

获取枚举值

// 枚举的基本定义
enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

// 点语法
console.log(Direction.UP) // => 0

// 方括号语法
console.log(Direction['UP']) // => 0

数值枚举

在定义枚举的时候,我们可以只定义部分值或者定义全部值,后边的值会依次进行递增

enum Direction {
  UP, // 0
  LEFT = 3, // 3
  RIGHT, // 4
  DOWN // 5
}
enum Direction {
  UP = 2, // 2
  LEFT = 3, // 3
  RIGHT = 5, // 5
  DOWN = 7// 7
}

数值枚举的值不仅仅可以是一个数字,也可以是一个值为数值的常量或返回数值的计算值

但如果数值枚举中的值的类型是常量或者计算值的时候,常量枚举将无法进行自动递增

所以此时必须为值为常量或计算值后的哪一个枚举成员进行初始化操作

// error
const ONE = 1

enum Direction {
  UP,
  LEFT = ONE,
  RIGHT, 
  DOWN
}

// success
const ONE = 1

enum Direction {
  UP,
  LEFT = ONE,
  RIGHT = 2,
  DOWN
}
// error
const getIndex = () => 3

enum Direction {
  UP,
  LEFT = getIndex(),
  RIGHT,
  DOWN
}

// success
const getIndex = () => 3

enum Direction {
  UP,
  LEFT = getIndex(),
  RIGHT = 3,
  DOWN
}
enum Direction {
  UP,
  LEFT = 1 + 2, // 注意: 这种写法,ts在编译的时候会视为普通数值进行看待
  RIGHT,
  DOWN
}

反向映射

我们可以通过我们定义的字段名获取到对应的枚举值

我们也可以通过对应的枚举值来获取到对应的字段名

enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

// 字段名获取枚举值
console.log(Direction.UP) // 0

// 枚举值获取字段名
console.log(Direction[1]) // 'LEFT'

字符串枚举

在字符串枚举中,每一个枚举值都是字符串或者当前枚举自身的字段名

在字符串枚举中,不可以使用常量,计算值或者其它枚举中的枚举成员

enum Foo {
  FOO = 'foo',
  BAR = 'bar',
  BAZ ='baz'
}
enum Foo {
  FOO = 'foo',
  BAR = 'bar',
  BAZ = FOO
}

console.log(Foo.FOO) // => foo
console.log(Foo.BAZ) // => foo
// error
enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

enum Foo {
  FOO = 'foo',
  BAR = 'bar',
  BAZ = Direction.UP
}
// error
const cBAZ = 'baz'

enum Foo {
  FOO = 'foo',
  BAR = 'bar',
  BAZ = cBAZ
}
// error
const fun = () => 'baz'

enum Foo {
  FOO = 'foo',
  BAR = 'bar',
  BAZ = fun()
}

异构枚举

枚举值既有字符串又有数值的枚举

如果一个枚举可以定义为数值枚举或字符串枚举的时候,推荐优先将枚举定义为字符串枚举或数组枚举

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

枚举成员类型

当一个枚举满足以下任一一个条件的时候,这个枚举中的每一个枚举成员以及这个枚举本身都可以作为TS中的类型进行使用

  1. 每一个枚举成员都没有赋初值
  2. 每一个枚举成员都是字符串
  3. 每一个枚举成员都是数值(可以是正数,也可以是负数)
enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

let up: Direction.UP

up = 0 // success
up = Direction.UP // success


// Direction <=> 0 | 1 | 2 | 3
// Direction就是四个枚举成员值组成的联合类型
let diection: Direction
diection = Direction.UP // success
diection = 3 // success

常量枚举

之前所定义的那些枚举,在TS编译后,会在原本的JS代码中添加对应的对象,这类枚举被称之为运行时枚举

enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

编译后

"use strict";
var Direction;
(function (Direction) {
    Direction[Direction["UP"] = 0] = "UP";
    Direction[Direction["LEFT"] = 1] = "LEFT";
    Direction[Direction["RIGHT"] = 2] = "RIGHT";
    Direction[Direction["DOWN"] = 3] = "DOWN";
})(Direction || (Direction = {}));

而有的时候我们仅仅只是希望提升代码的可读性,并不希望在编译后的代码中存在枚举对象,此时我们可以使用常量枚举

常量枚举编译后,会使用具体的值去替换使用了枚举的位置的变量,从而避免在比编译后生成对应的枚举对象

const enum Direction {
  UP,
  LEFT,
  RIGHT,
  DOWN
}

console.log(Direction.UP)

编译后

"use strict";
console.log(0 /* UP */);