TypeScript 系列 进阶篇 (三) 枚举
枚举通过关键字 enum
来声明,会同时得到一个类型和一个同名的值,该值为一组命名了的常量。TS
提供了基于数字和基于字符串的枚举,当然,这本该是基础篇的内容…
一、数字枚举
我们使用关键字 enum
来声明一个枚举。当我们不给初值时,就会自动初始化,值依次从 0
开始递增。
1 | enum Direction { |
如果我们初始化了某属性的数值,紧随其后的其它属性的数值则会根据 步长为1
的递增/递减 自动推算。
1 | enum Direction { |
枚举的使用也很简单。我们知道,声明枚举既得到值,也得到一个同名的类型。我们只需要通过属性来访问枚举内部的值,也能通过枚举的名字来作为类型使用。
1 | enum UserResponse { |
此外,枚举成员也能混用计算属性和数字 (当然,还有字符串),但需要注意:
- 枚举的第一个成员,如果没有初始化值,则被自动赋予数值
0
; - 枚举的其它成员,如果紧随一个数值成员之后,则他们的值会依次递增
1
; - 枚举的其它成员,如果不是紧随数值成员之后,则需要初始化其值(数值、字符串或计算属性);
二、字符串枚举
字符串枚举的成员,必须用 string 类型的字面量,或者其它枚举的成员来进行值的初始化。字符串枚举没有自增行为,因此,在我们为其初始化一个有意义的 string 类型的字面量之后,在调试中将会比数子枚举更加友好。
1 | enum Direction { |
三、异构枚举
将数字和字符串混合使用,即为异构枚举。但是,与这个听起来好像很高端的名字不一样,使用异构枚举往往没有声明对我们写代码没有什么帮助,建议避免这种写法。
1 | enum BooleanLikeHeterogeneousEnum { |
四、计算属性成员和常量成员
我们使用常量枚举表达式来初始化枚举的成员。符合以下条件的都是常量枚举表达式:
- 字面量枚举表达式;
- 对已经定义好的枚举成员的引用(可以是自身的成员,也可以是其它枚举的成员);
- 带括号的常量枚举表达式;
- 使用 + 、- 、~ 等一元运算符来操作的常量表达式;
+
,-
,*
,/
,%
,<<
,>>
,>>>
,&
,|
,^
等二元运算符来操作的常量表达式
如果常量枚举表达式发生运行时错误,则其值会变为NaN
或者 Infinity
。其它的所有情况下,枚举成员都被认为是计算属性成员。
1 | enum FileAccess { |
五、联合枚举 和 枚举成员的类型
字面量枚举成员:
- 任何的字符串字面量,如
"cc"
,"age
“等; - 任何的数值字面量,如
1
,99
等; - 任何一元运算符操作的数值字面量,如
-100
等。
当一个枚举的所有成员都是字面量枚举成员时,则枚举成员也会成为一个类型。
1 | enum ShapeKind { |
当一个枚举的所有成员都是字面量枚举成员时,则该枚举本身作为类型,等价于所有枚举成员的联合。
1 | enum E { |
六、运行时 与 编译时 的 枚举
1. 运行时的枚举是一个真实的对象。
2. 编译时的枚举有如下特性:
反向映射
在典型的对象中,我们通常使用属性名来获取值,这是从 key 到 value 的正向映射。然而在枚举中,我们也可以通过值来获取属性名,这便是反向映射。只有数字枚举才会有反向映射。
1
2
3
4
5
6enum E {
age = 18,
}
const a = E.age; // 18
const keyOfcc = E[a]; // "age",反向映射常量枚举
常量枚举的成员只能使用常量枚举表达式,且它们在编译期间会被完全删除。如下:
1
2
3
4
5
6
7
8
9
10
11
12
13const enum Direction {
Up,
Down,
Left,
Right,
}
let directions = [
Direction.Up,
Direction.Down,
Direction.Left,
Direction.Right,
];在编译之后,生成的代码中,该枚举会被删除,替换为相应的常量,生成如下代码:
1
2
3
4
5
6
7"use strict";
let directions = [
0 /* Up */,
1 /* Down */,
2 /* Left */,
3 /* Right */,
];常量枚举陷阱
陷阱就不提了,涉及到一些我目前的这些文章中没提到的概念。不够官方文档提供了两条解决办法。
完全不使用常量枚举。这可以解决陷阱问题,但是完全禁止了常量枚举的使用,对我们的项目并不友好;
不发布环境常量枚举,据说 TS 项目内部就是这么做的。
七、环境枚举
环境枚举用于描述一个已经存在的枚举的形状。与常规枚举不同的是,环境枚举的所有没有初始化的成员会被 TS 认为是计算属性成员。
1 | declare enum Enum { |
八、枚举 vs 对象
本来还在开开心心学枚举,直到看到官网上这么一句话:在现代TypeScript
中,我们或许不太需要枚举类型,因为可以通过对一个对象使用常量断言 as const
来替代枚举。甚至用对象的方式可能会更好,因为更贴近JS
语言习惯。???所以我花了大半天时间在官方文档上看枚举是为了啥???不管怎么样,还是把栗子搬过来:
1 | const enum EDirection { |
行吧,枚举咱就学到这里了,下一期我们来学习 namespace
,也许吧。下一篇再见!