TypeScript 之枚舉

landscape.jpg

枚舉

  • 使用枚舉我們可以定義一些帶名字的常量
  • 使用枚舉可以清晰地表達(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 的值為 1Down2, Left3殉挽,Right4丰涉。

我們還可以完全不使用初始化器:

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á)式求值后為 NaNInfinity,則會(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麸锉, barbaz
  • 任何數(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ì)算的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載碘梢,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者咬摇。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市煞躬,隨后出現(xiàn)的幾起案子肛鹏,更是在濱河造成了極大的恐慌,老刑警劉巖恩沛,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件在扰,死亡現(xiàn)場離奇詭異,居然都是意外死亡雷客,警方通過查閱死者的電腦和手機(jī)芒珠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來搅裙,“玉大人皱卓,你說我怎么就攤上這事〔看” “怎么了好爬?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長甥啄。 經(jīng)常有香客問我存炮,道長,這世上最難降的妖魔是什么蜈漓? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任穆桂,我火速辦了婚禮,結(jié)果婚禮上融虽,老公的妹妹穿的比我還像新娘享完。我一直安慰自己,他們只是感情好有额,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布般又。 她就那樣靜靜地躺著彼绷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪茴迁。 梳的紋絲不亂的頭發(fā)上寄悯,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音堕义,去河邊找鬼猜旬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛倦卖,可吹牛的內(nèi)容都是我干的洒擦。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼怕膛,長吁一口氣:“原來是場噩夢啊……” “哼熟嫩!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起褐捻,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤掸茅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后舍扰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡希坚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年边苹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片裁僧。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡个束,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出聊疲,到底是詐尸還是另有隱情茬底,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布获洲,位于F島的核電站阱表,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏贡珊。R本人自食惡果不足惜最爬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望门岔。 院中可真熱鬧爱致,春花似錦、人聲如沸寒随。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至互艾,卻和暖如春试和,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背忘朝。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工灰署, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人局嘁。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓溉箕,卻偏偏與公主長得像,于是被迫代替她去往敵國和親悦昵。 傳聞我的和親對象是個(gè)殘疾皇子肴茄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344