枚举
反向映射
我们可以通过 Enum[key]
或者 Enum.key
的方式获取到对应的值。typescript
还支持反向映射,即可以通过值来获取键,不过反向映射只支持数字枚举。下面是个例子:
enum Status {
Success = 200,
NotFound = 404,
Error = 500
}
console.log(Status.Success) // 200
console.log(Status[200]) // "Success"
console.log(Status[Status.Success]) // "Success"
枚举成员类型和联合枚举类型
如果一个枚举里所有成员的值都是字面量类型的值,那么这个枚举的每个成员和枚举本身都可以作为类型来使用.
字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为
- 任何字符串字面量(例如:
"foo", "bar", "baz"
)例如:enum E { A = 'foo' }
- 任何数字字面量(例如:
1, 100
)例如:enum E { A = 3, B = 4 }
- 应用了一元 -符号的数字字面量(例如:
-1, -100
)例如:enum E { A = 1 },enum E { A = -1 }
注意: 枚举类型与数字类型兼容,并且数字类型与枚举类型兼容。不同枚举类型之间是不兼容的.
// 示例1
enum State {
on,
off
}
const s: State = 123 // 数字允许
// 示例2
enum State {
on = 'abc',
off = 'def'
}
const s: State = 123 // 数字不允许
const s: State = 'abc' // 字符串不允许
const s: State = State.on // 允许
// 示例3
enum State {
on = 123,
off = 'def'
}
const s: State = 123 // 数字允许
const s: State = 111 // 数字允许
const s: State = '123' // 字符串不允许
const s: State = State.on // 允许
枚举成员类型
把符合条件的枚举成员作为类型来使用,例子:
enum ShapeKind {
Circle,
Square
}
interface Circle {
kind: ShapeKind.Circle // 使用 ShapeKind.Circle 作为类型,指定接口须有 kind 字段,且类型为 ShapeKind.Circle
radius: number
}
interface Square {
kind: ShapeKind.Square // 同上
sideLength: number
}
let c: Circle = {
kind: ShapeKind.Square, // Error! 因为接口 Circle 的 kind 被指定为 ShapeKind.Circle类型,所以这里会报错
radius: 100
}
联合枚举类型
符合条件的枚举本身可以看作是一个包含所有成员的联合类型,下面例子:
enum E {
Foo,
Bar,
}
function f(x: E) { // 此时的 E 相当于 Foo | Bar
if (x !== E.Foo || x !== E.Bar) {
// ~~~~~~~~~~~
// Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'.
}
}
这个例子里,我们先检查 x
是否不是 E.Foo
。 如果通过了这个检查,然后 ||
会发生短路效果, if
语句体里的内容会被执行。 然而,这个检查没有通过,那么 x
则 只能为 E.Foo
,因此没理由再去检查它是否为 E.Bar
。
// 枚举 Status 里有两个状态
enum Status {
Off,
On
}
// 枚举 Animal 里有两个动物
enum Animal {
Cat,
Dog
}
// 接口 Light 中定义 status字段,它是 Status 类型,可以是 Off 或者 On 状态
interface Light {
status: Status
}
let lg1: Light = {
status: Status.Off // 正确
}
let lg2: Light = {
status: Animal.Cat // error 不能将类型 Animal.Cat 分配给类型 Status
}
运行时的枚举
枚举是在运行时真正存在的对象,可以把枚举当作对象使用:
enum E {
A,
B
}
function func(obj: { A: number }): number {
return obj.A
}
console.log(func(E)) // 0
代码中,声明了一个函数 func,它的参数是一个对象,且必须包含属性名为 A
的属性,A
的值为数值类型。当调用函数 func
时,把枚举 E
当作符合条件的实参传入,正确运行。
const
枚举
定义枚举,在经过编译器编译后是一个对象,这个对象我们可以在程序运行时使用。但有时定义枚举可能只是为了让程序可读性更好,而不需要编译后的代码,即不需要编译成对象。typescript
中考虑到这种情况,所以加入了 const enum
(完全嵌入的枚举)。typescript
官网有一个在线编译器,来看看下面的例子:
// 普通枚举
enum Status{
Off,
On
}
// const枚举
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 */;
可以看到编译后的代码中并没有像创建 Status
一样创建了 Animal
,而是直接把 Animal
中 Dog
值 0
替换到表达式中 Animal.Dog
的位置,这样就节省了生成代码的开销。
特点
const
枚举在编译后因为没有生成相应的对象,因此运行时是不可访问当前枚举对象的,只允许访问枚举成员的值.
// 修饰符 + 关键词 + 枚举名称
const enum NumEnum { a, b, c }
const obj = NumEnum // => Error,运行时不存在
function returnNumber (obj: { a: number }): number {
return obj.a
}
returnNumber(NumEnum) // => Error,运行时不存在
const A = NumEnum['0'] // => Error,没有生成反向映射,所以不存在该属性
const B = NumEnum['b'] // 正确 => 0/* b */
"compilerOptions": {
// 保留 const 和 enum 声明该项为 true
"preserveConstEnums": true
}
// 或者在命令行中添加了 --preserveConstEnums 指令,均会让当前 const 枚举转变为普通枚举
// 即会生成反向映射表;在实际开发中,一般情况你可能需要禁止掉该项,除非想要用于调试;
基于 const
枚举的特点,如果你只是为了生成一组常量并且只需要获取某一个常量, 从性能方面考虑,const 枚举是首选.当然如果你在 tsconfig.json 中指定下面选项:
"compilerOptions": {
// 保留 const 和 enum 声明该项为 true
"preserveConstEnums": true
}
或者在命令行中添加了 --preserveConstEnums
指令,均会让当前 const
枚举转变为普通枚举, 即会生成反向映射表. 在实际开发中,一般情况你可能需要禁止掉该项,除非想要用于调试.