基础类型中简单介绍过枚举,用于将取值限定在一定范围内或定义一些不具有语义性的常量,使之可以清晰的表达意图。
数字枚举
enum Colors {
red,
pink,
blue
}
编译结果:
var Colors
;(function (Colors) {
Colors[(Colors['red'] = 0)] = 'red'
Colors[(Colors['pink'] = 1)] = 'pink'
Colors[(Colors['blue'] = 2)] = 'blue'
})(Colors || (Colors = {}))
枚举成员的值默认从 0 开始自增,同时也会对枚举值到枚举名反向映射。
字符串枚举
在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。不可以使用常量枚举或计算值。
enum Colors {
red = 'RED',
pink = 'PINK',
blue = red
}
Colors.blue // 'RED'
编译结果:
var Colors
;(function (Colors) {
Colors['red'] = 'RED'
Colors['pink'] = 'PINK'
Colors['blue'] = 'RED'
})(Colors || (Colors = {}))
由于字符串枚举没有自增行为,所以字符串枚举可以很好的序列化。 换句话说,数字枚举的值通常并不能表达有用的信息(尽管反向映射会有所帮助),但字符串枚举允许提供一个运行时有意义的并且可读的值,独立于枚举成员的名字。
手动赋值
可以对单个或者全部枚举成员手动赋值。
当对某个枚举成员赋值后,该枚举之后未手动赋值的枚举项会接着递增。
enum Colors {
red,
pink = 3,
blue
}
Colors.blue // 4
enum Colors {
red = 1,
pink = 3,
blue = 5
}
Colors.blue // 5
如果未手动赋值的枚举项与手动赋值的重复了,TypeScript 编译时不会进行限制:
enum Colors {
red = 3,
pink = 1,
blue,
black,
orange
}
Colors.red // 3
Colors.black // 3
Colors[3] // 'black'
Colors[3] // 'black'
以上枚举递增到 3 的时候与前面的 red 的取值重复了,但是 TypeScript 并没有报错,导致 Colors[3] 的值先是 "red",而后又被 "black" 覆盖。编译的结果是如下,所以使用的时候需要注意,不要出现覆盖的情况。
var Colors
;(function (Colors) {
Colors[(Colors['red'] = 3)] = 'red'
Colors[(Colors['pink'] = 1)] = 'pink'
Colors[(Colors['blue'] = 2)] = 'blue'
Colors[(Colors['black'] = 3)] = 'black'
Colors[(Colors['orange'] = 4)] = 'orange'
})(Colors || (Colors = {}))
此外手动赋值的枚举项也可以为小数或负数,之后未手动赋值的项的递增步长仍为 1:
enum Colors {
red = 1.5,
pink,
blue,
black = -1,
orange
}
Colors.pink // 2.5
Colors.orange // 0
常数项和可计算成员
当给某一个枚举成员设置常量或计算所得值后,其之后的成员必须手动初始化。
可计算成员的意思就是:初始化枚举成员时,可使用表达式、函数等方式动态求值,还可以是对之前定义的常量枚举成员的引用。
常数项
const num = 2
enum Colors {
pink = 4,
red = num,
blue = 7
}
以上将常量 num 赋值给枚举成员 red,则其之后的成员必须初始化,否则编译报错:
const num = 2
enum Colors {
pink = 4,
red = num,
blue
}
// Enum member must have initializer.
计算所得值
function getNum() {
return 3
}
enum Colors {
red = 1,
blue = getNum(),
pink = 6
}
enum Colors {
red = 1,
blue = 'sina'.length,
pink = 6
}
反向映射
除了创建一个以属性名做为对象成员的对象之外,数字枚举的成员还具有反向映射,从枚举值到枚举名。如下:
enum Colors {
red
}
Colors.red // 0
Colors[0] // 'red'
编译结果:
var Colors
;(function (Colors) {
Colors[(Colors['red'] = 0)] = 'red'
})(Colors || (Colors = {}))
ts 编译生成的 js 代码中,数字枚举类型被编译成一个对象,包含正向映射( key -> value)和反向映射( value -> key)。
注意:字符串枚举的成员不会生成反向映射。
异构枚举
枚举可以混合字符串和数字成员(不建议)
enum Colors {
red = 'RED',
pink = 'PINK',
blue = 4
}
运行时枚举
枚举在运行时是真正存在的对象,因为 ts 会将枚举编译成 js 对象。
enum Colors {
red
}
function bar(obj, key) {
return obj[key]
}
bar(Colors, 'red')
编译结果:
var Colors
;(function (Colors) {
Colors[(Colors['red'] = 0)] = 'red'
})(Colors || (Colors = {}))
function bar(obj, key) {
return obj[key]
}
bar(Colors, 'red')
const(常量)枚举
常量枚举通过在枚举上使用 const 修饰符来定义。
const enum Colors {
red = 4
}
function bar() {
return Colors.red
}
bar() // 4
常量枚举与普通枚举不一样它会在编译阶段被删除。因为普通枚举会被编译成 js 对象,可以将常量枚举理解成在编译阶段首先生成 js 对象,然后将所有的枚举值计算出来,然后删除对象。
编译结果:
function bar() {
return 4 /* red */
}
bar()
所以在运行时不可以使用常量枚举。比如:
const enum Colors {
red
}
console.log(Colors)
// 'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query.
// ("const" 枚举仅可在属性、索引访问表达式、导入声明的右侧、导出分配或类型查询中使用。)
联合枚举与枚举成员的类型
首先解释字面量枚举成员。
字面量枚举成员是指不带有初始值的常量枚举成员,或者是值被初始化为以下三种情况的枚举成员。
- 任何字符串字面量;
- 任何数字字面量
- 应用了一元
-
符号的数字字面量
- 不带有初始值的常量枚举成员
enum Colors {
red
}
- 任何字符串字面量:
enum Colors {
red = 'RED'
}
- 任何数字字面量:
enum Colors {
red = 1
}
- 应用了一元
-
符号的数字字面量:
enum Colors {
red = -10
}
当所有枚举成员都拥有字面量枚举值时,枚举成员或枚举可以当作类型使用。
枚举成员类型
枚举成员类型是指将枚举的某些成员当作类型使用。
enum Colors {
red = 'RED'
pink = 'PINK'
}
interface Tomato {
color: Colors.red
}
const tomato: Tomato = {
color: Colors.red
// color: Colors.pink
// Type 'Colors.pink' is not assignable to type 'Colors.red'.
}
枚举类型
枚举类型本身变成了每个枚举成员的联合类型。
enum Colors {
red = 'RED',
pink = 'PINK'
}
interface IColor {
color: Colors
}
const tomato: IColor = {
color: Colors.red
}