枚舉
- 使用枚舉我們可以定義一些帶名字的常量
- 使用枚舉可以清晰地表達(dá)意圖或者創(chuàng)建一組有區(qū)別的用例饼拍。
-
TypeScript
支持基于數(shù)字的和基于字符串的枚舉。
數(shù)字枚舉
首先讓我能看看數(shù)字枚舉南吮,如果你使用過其他編程語言應(yīng)該會(huì)很熟悉。
enum Direction {
Up = 1,
Down,
Left,
Right
}
如上逆皮,我們定義了一個(gè)數(shù)字枚舉由蘑,Up
使用初始化為 1
。其余成員會(huì)從 1
開始自動(dòng)增長帮碰。換句話說相味, Direction.Up
的值為 1
,Down
為 2
, Left
為 3
殉挽,Right
為 4
丰涉。
我們還可以完全不使用初始化器:
enum Direction {
Up,
Down,
Left,
Right
}
現(xiàn)在, Up
的值為 0
斯碌,Down
的值為 1
等等一死。當(dāng)我們不在乎成員的值的時(shí)候,這種自增長的行為是很有用處的傻唾,但是要注意每個(gè)枚舉成員的值都是不同的投慈。
使用枚舉很簡單:
- 通過枚舉的屬性來訪問枚舉成員
- 通過枚舉的名字來訪問枚舉類型
enum Response {
No = 0,
Yes = 1
}
function respond(recipient: string, message: Response): void {}
respond('Princess Caroline', Response.Yes);
數(shù)字枚舉可以被混入到 計(jì)算過的和常量成員
。簡短地說冠骄,不帶初始化的枚舉或者被放在第一的位置伪煤,或者被放在使用了數(shù)字常量或其他常量初始化了的枚舉后面。換句話說凛辣,下面的情況是不被允許的:
enum E = {
A = getSomeValue(),
B // 錯(cuò)誤!'A'不是常量初始化的抱既,所以'B'需要一個(gè)初始化器
}
字符串枚舉
字符串枚舉的概念很簡單,但是有細(xì)微的運(yùn)行時(shí)的差別蟀给。在一個(gè)字符串枚舉里蝙砌,每個(gè)成員都必須用字符串字面量,或另一個(gè)字符串枚舉成員進(jìn)行初始化跋理。
enum Direction {
Up = 'Up',
Down = 'Down',
Left = 'Left',
Right = 'Right'
}
由于字符串枚舉沒有自增長的行為择克,字符串枚舉可以很好的序列化。換句話說前普,如果你正在調(diào)試且必須要讀一個(gè)數(shù)字枚舉的運(yùn)行時(shí)的值肚邢,這個(gè)值通常是很難讀的 - 它并不能表達(dá)有用的信息(盡管 反向映射
會(huì)有所幫助),字符串枚舉允許提供一個(gè)運(yùn)行時(shí)有意義并且可讀的值,獨(dú)立于枚舉成員的名字骡湖。
異構(gòu)枚舉 Heterogeneous enums
從技術(shù)的角度來說贱纠,枚舉可以混合數(shù)字和字符串成員,但是似乎你并不會(huì)這么做:
enum BooleanLikeHeterogeneousEnum {
No = 0,
Yes = 'Yes'
}
除非你真的想要利用 JavaScript
運(yùn)行時(shí)的行為响蕴,否則我們不建議這樣做谆焊。
計(jì)算的和常量成員
每個(gè)枚舉成員都帶有一個(gè)值,它可以是 常量 或 計(jì)算出來的浦夷。當(dāng)滿足如下條件時(shí)辖试,枚舉成員被當(dāng)作是常量:
- 它是枚舉的第一個(gè)成員且沒有初始化器,這種情況下它被賦值
0
:
// E.X is constant
enum E {
X
}
- 它不帶有初始化器且它之前的枚舉成員是一個(gè) 數(shù)字常量劈狐。這種情況下罐孝,當(dāng)前枚舉成員的值為它上一個(gè)枚舉成員的值加
1
。
// All enum members in 'E1' and 'E2' are constant.
enum E1 {
X,
Y,
Z
}
enum E2 {
A = 1,
B,
C
}
- 枚舉成員使用 常量枚舉表達(dá)式 初始化肥缔。常量枚舉表達(dá)式是
TypeScript
的子集莲兢,它可以在編譯階段求值。當(dāng)一個(gè)表達(dá)式滿足下面條件之一時(shí)续膳,它就是一個(gè)常量枚舉表達(dá)式:- 一個(gè)枚舉表達(dá)式字面量(主要是字符串字面量或數(shù)字字面量)
- 一個(gè)對之前定義的常量枚舉成員的引用(可以是在不同的枚舉類型中定義的)
- 帶括號的常量枚舉表達(dá)式
- 一元運(yùn)算符
+
改艇,-
,~
其中之一應(yīng)用在了常量枚舉表達(dá)式 - 常量枚舉表達(dá)式作為二元運(yùn)算符
+
坟岔,-
遣耍,*
,/
炮车,%
舵变,<<
,>>
瘦穆,>>>
纪隙,&
,|
扛或,^
的操作對象绵咱。若常量枚舉表達(dá)式求值后為NaN
或Infinity
,則會(huì)在編譯階段報(bào)錯(cuò)熙兔。
所有其它情況的枚舉成員被當(dāng)作是需要計(jì)算得出的值悲伶。
enum FileAccess {
// constant members
None,
Read = 1 << 1,
Write = 1 << 2,
ReadWrite = Read | Write,
// computed member
G = '123'.length
}
聯(lián)合枚舉與枚舉成員的類型
存在一種特殊的非計(jì)算的常量枚舉成員的子集:字面量枚舉成員。字面量枚舉成員是指不帶有初始值的常量枚舉成員住涉,或者是值被初始化為:
- 任何字符串字面量(例如:
foo
麸锉,bar
,baz
) - 任何數(shù)字字面量(例如:
1
舆声,100
) - 應(yīng)用了一元
-
符號的數(shù)字字面量(例如:-1
花沉,-100
)
當(dāng)所有枚舉成員都擁有字面量枚舉值時(shí)柳爽,它就帶有了一種特殊的語義。
首先碱屁,枚舉成員成為了類型磷脯! 例如,我們可以說某些成員 只能是枚舉成員的值:
enum ShapeKind {
Circle,
Square
}
interface Circle {
kind: ShapeKind.Circle;
radius: number;
}
interface Square {
kind: ShapeKind.Square;
sideLength: number;
}
let c: Circle = {
kind: ShapeKind.Square,
// Error !
rasius: 100
};
另一個(gè)變化是枚舉類型本身變成了每個(gè)枚舉成員的 聯(lián)合娩脾。雖然我們還沒有討論聯(lián)合類型赵誓,但你只要知道通過聯(lián)合枚舉,類型系統(tǒng)能夠利用這樣一個(gè)事實(shí)柿赊,它可以知道枚舉里的值的集合架曹。因此,TypeScript
能夠捕獲在比較值的時(shí)候犯的愚蠢的錯(cuò)誤闹瞧。例如:
enmu E {
Foo,
Bar
}
function f(x: E) {
if (x !== E.Foo || x !== E.Bar) {
// Error! Operator '!==' cannot be applied to types 'E.Foo' and 'E.Bar'
}
}
在這個(gè)例子里,我們先檢查 x
是否不是 E.Foo
展辞。如果通過了這個(gè)檢查奥邮,然后 ||
會(huì)發(fā)生短路效果,if
語句體里的內(nèi)容會(huì)被執(zhí)行罗珍。然而洽腺,這個(gè)檢查沒有通過,那么x
只能為 E.Foo
覆旱,因此沒理由再去檢查它是否為 E.Bar
蘸朋。
運(yùn)行時(shí)的枚舉
枚舉是在運(yùn)行時(shí)真正存在的對象。例如下面的枚舉:
enum E {
X,
Y,
Z
}
實(shí)際上可以傳遞給函數(shù):
function f(obj: { X: number }) {
return obj.X;
}
// Works, since 'E' has a property named 'X' which is a number.
f(E);
反向映射
除了創(chuàng)建一個(gè)以屬性名作為對象成員的對象之外扣唱,數(shù)字枚舉成員還具有了 反向映射藕坯,從枚舉值到枚舉名字。例如噪沙,在下面的例子中:
enum Enum {
A
}
let a = Enum.A;
let nameOfA = Enum[a]; // A
TypeScript
可能會(huì)將這段代碼編譯為下面的 JavaScript
:
var Enum;
(function(Enum) {
Enum[(Enum['A'] = 0)] = 'A';
})(Enum || (Enum = {}));
var a = Enum.A;
var nameOfA = Enum[a];
生成的代碼中炼彪,枚舉類型被編譯成一個(gè)對象,它包含了正向映射(name
-> value
)和 反向映射(value
-> name
)正歼。引用枚舉成員總會(huì)生成為對象屬性訪問并且永遠(yuǎn)也不會(huì)內(nèi)聯(lián)代碼辐马。
要注意的是,不會(huì)為 字符串枚舉成員生成反向映射局义。
const
枚舉
大多數(shù)情況下喜爷,枚舉是十分有效的方案。然而在某些情況下需求很嚴(yán)格萄唇。為了避免在額外生成的代碼上的開銷和額外的非直接的對枚舉成員的訪問檩帐。我們可以使用 const
枚舉。常量枚舉通過在枚舉上使用 const
修飾符來定義另萤。
const enum Enum {
A = 1,
B = A * 2
}
常量枚舉只能使用常量枚舉白噢大事轿塔,并且不同于常規(guī)的枚舉,它們在編譯階段會(huì)被刪除。常量枚舉成員字在使用的地方會(huì)被內(nèi)聯(lián)起來勾缭。之所以可以這么做是因?yàn)樽嵴希A棵杜e不允許包含計(jì)算成員。
const enum Directions {
Up,
Down,
Left,
Right
}
let directions = [
Directions.Up,
Directions.Down,
Directions.Left,
Directions.Right
];
生成后的代碼為:
var directions = [0 /*Up*/, 1 /*Down*/, 2 /*Left*/, 3 /*Right*/];
外部枚舉
外部枚舉用來描述已經(jīng)存在的枚舉類型的形狀俩由。
declare enum Enum {
A = 1,
B,
C = 2
}
外部枚舉和非外部枚舉之間有一個(gè)重要的區(qū)別毒嫡,在正常的枚舉里,沒有初始化方法的成員被當(dāng)成常數(shù)成員幻梯。對于非常數(shù)的外部枚舉而言兜畸,沒有初始化方法時(shí)被當(dāng)做需要經(jīng)過計(jì)算的。