深入枚举
大约 5 分钟TypescriptTypescript
提示
- 数字枚举
- 反向枚举
- 字符串枚举
- 异构枚举
- 异构枚举
- 联合枚举类型
- 运行时的枚举
- const enum
一、前言
枚举是 TypeScript 新增加的一种数据类型,这在其他很多语言中很常见,但是 JavaScript 却没有。使用枚举,我们可以给一些难以理解的常量赋予一组具有意义的直观的名字,使其更为直观,你可以理解枚举就是一个字典。枚举使用enum
关键字定义,TypeScript 支持数字和字符串的枚举。
二、TypeScript 枚举类型
数字枚举
- 数字枚举在定义值的时候,可以使用计算值和常量
- 注意,如果某个字段使用了计算值或常量
简单例子:
enum Status {// 这里你的TSLint可能会报一个:枚举声明只能与命名空间或其他枚举声明合并。这样的错误,不影响编译
Uploading,
Success,
Failed
}
console.log(Status.Uploading); // 0
console.log(Status["Success"]); // 1
console.log(Status.Failed); // 2
上面例子使用enum
关键字定义了一个枚举值Status
,使用它的枚举值的元素值时,就像访问对象的属性一样。例如:console.log(Status.Uploading)
或者console.log(Status[Uploading])
。默认情况下,Uploading 的初始值为 0,其余的成员会从1开始递增。上面的代码经过编译后会生成以下代码(可以去 TypeScript官方文档提供的在线练习场 试试):
"use strict";
var Status;
(function (Status) {
Direction[(Status["Uploading"] = 0)] = "Uploading";
Direction[(Status["Success"] = 1)] = "Success";
Direction[(Status["Failed"] = 2)] = "Failed";
})(Status || (Status = {}));
把上面的例子指定索引值的话只需下面这样做:
enum Status {
Uploading = 3,
Success,
Failed
}
注意:如果某个字段使用了计算值或常量,那么该字段后面紧接着的字段必须设置初始值,不能使用默认的递增值。
const getValue = () => {
return 0;
};
enum ErrorIndex {
a = getValue(),
b, // error 枚举成员必须具有初始化的值
c
}
enum RightIndex {
a = getValue(),
b = 1,
c
}
const Start = 1;
enum Index {
a = Start,
b, // error 枚举成员必须具有初始化的值
c
}
缺点:
日志输出
/* * 在输出数字枚举的成员只会看到数字 */ enum NoYes { No, Yes } console.log(NoYes.No); console.log(NoYes.Yes); // Output: // 0 // 1
松散型检查
/* * 将枚举用作类型时,允许的值不只是枚举成员的值 – 可以接受任何数字 */ enum NoYes { No, Yes } function func(noYes: NoYes) {} func(33); // no error!
建议:使用字符串枚举
反向枚举
- 定义一个枚举值的时候,可以通过Enum[key]或者Enun.key的形式获取到对应的值value
- 反向映射只支持数据枚举,字符串枚举是不支持的
简单例子:
enum Status {
Success = 200,
NotFound = 404,
Error = 500
}
console.log(Status["Success"]); // 200
console.log(Status[200]); // 'Success'
console.log(Status[Status["Success"]]); // 'Success'
字符串枚举
- 字符串枚举值要求每个字段的值都必须是字符串字面量,或者是该枚举值中另一个字符串枚举成员
- 字符串常量不能使用常量或者计算值
简单例子:
enum Direction {
NORTH = "NORTH",
SOUTH = "SOUTH",
EAST = "EAST",
WEST = "WEST",
}
异构枚举
异构枚举就是枚举值中成员值既有数据类型又有字符串类型,简单例子:
enum Enum {
A,
B,
C = "C",
D = "D",
E = 8,
F,
}
枚举成员类型
可以把符合条件的枚举值的成员作为类型来使用,简单例子:
enum Animal {
Dog = 1,
Cat = 2
}
interface Dog {
type: Animal.Dog; // 这里使用Animal.Dog作为类型,指定接口Dog的必须有一个type字段,且类型为Animal.Dog
}
interface Cat {
type: Animal.Cat; // 这里同上
}
let cat1: Cat = {
type: Animal.Dog // error [ts] 不能将类型“Animal.Dog”分配给类型“Animal.Cat”
};
let dog: Dog = {
type: Animal.Dog
};
联合枚举类型
当枚举值符合条件时,这个枚举值就可以看作是一个包含所有成员的联合类型,简单例子:
enum Status {
Off,
On
}
interface Light {
status: Status;
}
enum Animal {
Dog = 1,
Cat = 2
}
const light1: Light = {
status: Animal.Dog // error 不能将类型“Animal.Dog”分配给类型“Status”
};
const light2: Light = {
status: Status.Off
};
const light3: Light = {
status: Status.On
};
运行时的枚举
枚举在编译成JavaScript之后实际是一个对象,简单例子:
enum E {
A,
B
}
const getIndex = (enumObj: { A: number }): number => {
return enumObj.A;
};
console.log(getIndex(E)); // 0
它运行时相当于下面这个对象:
{
0: "A",
1: "B",
A: 0,
B: 1
}
const enum
TypeScript
在 1.4 新增 ,在枚举前加上const
关键字,编译后的代码不会创建这个对象,只是会从枚举里拿到相应的值进行替换。简单例子:
enum Status {
Off,
On
}
const enum Animal {
Dog,
Cat
}
const status = Status.On;
const animal = Animal.Dog;
上面例子编译后变成这样的:
var Status;
(function(Status) {
Status[(Status["Off"] = 0)] = "Off";
Status[(Status["On"] = 1)] = "On";
})(Status || (Status = {}));
var status = Status.On;
var animal = 0; /* Dog */
三、枚举用例
多个常量
// Log level:
const off = Symbol('off');
const info = Symbol('info');
const warn = Symbol('warn');
const error = Symbol('error');
// 使用 enum 定义
enum LogLevel {
off = 'off',
info = 'info',
warn = 'warn',
error = 'error',
}
相比布尔值来说更具有自我描述性
有序列表和无序列表
enum ListKind { ordered, unordered } class List2 { listKind: ListKind; // ··· }
失败与成功
enum ResultStatus { failure, success } class Result { status: ResultStatus; // ··· }
更安全的字符串常量
enum Globalness {
Global = 'g',
notGlobal = '',
}
function createRegExp(source: string, globalness = Globalness.notGlobal) {
return new RegExp(source, 'u' + globalness);
}
assert.deepEqual(
createRegExp('abc', Globalness.Global),
/abc/ug);