How do the different enum variants work in TypeScript?

There are four different aspects to enums in TypeScript you need to be aware of. First, some definitions:
"lookup object"
If you write this enum:
enum Foo { X, Y }

TypeScript will emit the following object:
var Foo;(function (Foo) { Foo[Foo["X"] = 0] = "X"; Foo[Foo["Y"] = 1] = "Y";})(Foo || (Foo = {}));

I'll refer to this as the lookup object. Its purpose is twofold: to serve as a mapping from strings to numbers, e.g. when writing Foo.X
or Foo['X']
, and to serve as a mapping from numbers to strings. That reverse mapping is useful for debugging or logging purposes -- you will often have the value 0
or 1
and want to get the corresponding string "X"
or "Y"
.
"declare" or "ambient"
In TypeScript, you can "declare" things that the compiler should know about, but not actually emit code for. This is useful when you have libraries like jQuery that define some object (e.g. $
) that you want type information about, but don't need any code created by the compiler. The spec and other documentation refers to declarations made this way as being in an "ambient" context; it is important to note that all declarations in a .d.ts
file are "ambient" (either requiring an explicit declare
modifier or having it implicitly, depending on the declaration type).
"inlining"
For performance and code size reasons, it's often preferable to have a reference to an enum member replaced by its numeric equivalent when compiled:
enum Foo { X = 4 }var y = Foo.X; // emits "var y = 4";

The spec calls this substitution, I will call it inlining because it sounds cooler. Sometimes you will not want enum members to be inlined, for example because the enum value might change in a future version of the API.
Enums, how do they work?
Let's break this down by each aspect of an enum. Unfortunately, each of these four sections is going to reference terms from all of the others, so you'll probably need to read this whole thing more than once.
computed vs non-computed (constant)
Enum members can either be computed or not. The spec calls non-computed members constant, but I'll call them non-computed to avoid confusion with const.
A computed enum member is one whose value is not known at compile-time. References to computed members cannot be inlined, of course. Conversely, a non-computed enum member is once whose value is known at compile-time. References to non-computed members are always inlined.
Which enum members are computed and which are non-computed? First, all members of a const
enum are constant (i.e. non-computed), as the name implies. For a non-const enum, it depends on whether you're looking at an ambient (declare) enum or a non-ambient enum.
A member of a declare enum
(i.e. ambient enum) is constant if and only if it has an initializer. Otherwise, it is computed. Note that in a declare enum
, only numeric initializers are allowed. Example:
declare enum Foo { X, // Computed Y = 2, // Non-computed Z, // Computed! Not 3! Careful! Q = 1 + 1 // Error}

Finally, members of non-declare non-const enums are always considered to be computed. However, their initializing expressions are reduced down to constants if they're computable at compile-time. This means non-const enum members are never inlined (this behavior changed in TypeScript 1.5, see "Changes in TypeScript" at the bottom)
const vs non-const
const
An enum declaration can have the const
modifier. If an enum is const
, all references to its members inlined.
const enum Foo { A = 4 }var x = Foo.A; // emitted as "var x = 4;", always

const enums do not produce a lookup object when compiled. For this reason, it is an error to reference Foo
in the above code except as part of a member reference. No Foo
object will be present at runtime.
non-const
If an enum declaration does not have the const
modifier, references to its members are inlined only if the member is non-computed. A non-const, non-declare enum will produce a lookup object.
declare (ambient) vs non-declare
An important preface is that declare
in TypeScript has a very specific meaning: This object exists somewhere else. It's for describing existing objects. Using declare
to define objects that don't actually exist can have bad consequences; we'll explore those later.
declare
A declare enum
will not emit a lookup object. References to its members are inlined if those members are computed (see above on computed vs non-computed).
It's important to note that other forms of reference to a declare enum
are allowed, e.g. this code is not a compile error but will fail at runtime:
// Note: Assume no other file has actually created a Foo var at runtimedeclare enum Foo { Bar } var s = 'Bar';var b = Foo[s]; // Fails

This error falls under the category of "Don't lie to the compiler". If you don't have an object named Foo
at runtime, don't write declare enum Foo
!
A declare const enum
is not different from a const enum
, except in the case of --preserveConstEnums (see below).
non-declare
A non-declare enum produces a lookup object if it is not const
. Inlining is described above.
--preserveConstEnums flag
This flag has exactly one effect: non-declare const enums will emit a lookup object. Inlining is not affected. This is useful for debugging.
Common Errors
The most common mistake is to use a declare enum
when a regular enum
or const enum
would be more appropriate. A common form is this:
module MyModule { // Claiming this enum exists with 'declare', but it doesn't... export declare enum Lies { Foo = 0, Bar = 1 } var x = Lies.Foo; // Depend on inlining}module SomeOtherCode { // x ends up as 'undefined' at runtime import x = MyModule.Lies; // Try to use lookup object, which ought to exist // runtime error, canot read property 0 of undefined console.log(x[x.Foo]);}

Remember the golden rule: Never declare
things that don't actually exist
. Use const enum
if you always want inlining, or enum
if you want the lookup object.
Changes in TypeScript
Between TypeScript 1.4 and 1.5, there was a change in the behavior (see https://github.com/Microsoft/TypeScript/issues/2183) to make all members of non-declare non-const enums be treated as computed, even if they're explicitly initialized with a literal. This "unsplit the baby", so to speak, making the inlining behavior more predictable and more cleanly separating the concept of const enum
from regular enum
. Prior to this change, non-computed members of non-const enums were inlined more aggressively.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末邢隧,一起剝皮案震驚了整個濱河市耳幢,隨后出現(xiàn)的幾起案子碉京,更是在濱河造成了極大的恐慌唤反,老刑警劉巖豪治,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件夺英,死亡現(xiàn)場離奇詭異明垢,居然都是意外死亡神得,警方通過查閱死者的電腦和手機奈籽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門饥侵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人衣屏,你說我怎么就攤上這事躏升。” “怎么了狼忱?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵膨疏,是天一觀的道長。 經(jīng)常有香客問我藕赞,道長成肘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任斧蜕,我火速辦了婚禮双霍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘批销。我一直安慰自己洒闸,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布均芽。 她就那樣靜靜地躺著丘逸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪掀宋。 梳的紋絲不亂的頭發(fā)上深纲,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天仲锄,我揣著相機與錄音,去河邊找鬼湃鹊。 笑死儒喊,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的币呵。 我是一名探鬼主播怀愧,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼余赢!你這毒婦竟也來了芯义?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤妻柒,失蹤者是張志新(化名)和其女友劉穎扛拨,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體举塔,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡鬼癣,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了啤贩。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡拜秧,死狀恐怖痹屹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枉氮,我是刑警寧澤志衍,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站聊替,受9級特大地震影響楼肪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜惹悄,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一春叫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧泣港,春花似錦暂殖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至坡氯,卻和暖如春晨横,著一層夾襖步出監(jiān)牢的瞬間洋腮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工手形, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留啥供,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓叁幢,卻偏偏與公主長得像滤灯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子曼玩,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Correctness AdapterViewChildren Summary: AdapterViews can...
    MarcusMa閱讀 8,878評論 0 6
  • 今天發(fā)生了點事鳞骤,出去了剛回。 所以今天就整理自己的公眾號吧黍判! 一開始有47個公眾號豫尽,但其實有部分是完全不會看了,所...
    新奇小姐閱讀 217評論 0 1
  • 16年底因事出差到濟南顷帖,傍晚繞護城河走了一段美旧,感覺良好,那時就規(guī)化一定找機會圍著護城河跑一次贬墩。春節(jié)后機會來了榴嗅,因要...
    東方神鹿閱讀 568評論 0 0
  • 8月16日 第九天 ?奇臺縣江布拉克景區(qū)。萬畝麥田陶舞,正直金秋時節(jié)嗽测,金黃色的成熟麥田,一望無際肿孵,真是美景一世看不透...
    吳淇玥閱讀 690評論 0 2
  • 最近沒有看書停做,電視劇倒是緊追不止(近期大火的《我的前半生》)晤愧。 心里一直想著不能放棄學(xué)習(xí)和輸出,但被現(xiàn)實諸多所牽絆...
    紅鸞星說閱讀 1,434評論 0 2