TypeScriptの列挙型には、注意する必要がある4つの異なる側面があります。まず、いくつかの定義:
「ルックアップオブジェクト」
この列挙型を記述する場合:
enum Foo { X, Y }
TypeScriptは次のオブジェクトを発行します。
var Foo;
(function (Foo) {
Foo[Foo["X"] = 0] = "X";
Foo[Foo["Y"] = 1] = "Y";
})(Foo || (Foo = {}));
これをルックアップオブジェクトと呼びます。その目的は2つあり:からのマッピングとして機能するように、文字列に数字書くときなど、Foo.X
またはFoo['X']
、およびからのマッピングとして機能するように数値を文字列。この逆マッピングは、デバッグやロギングの目的で役立ちます。多くの場合、値を持っている0
か1
、対応する文字列"X"
or を取得したいでしょう"Y"
。
「宣言」または「周囲」
TypeScriptでは、コンパイラが知っておくべきことを "宣言"できますが、実際にはコードを発行しません。これは$
、型情報が必要なオブジェクト(など)を定義するjQueryのようなライブラリーがあるが、コンパイラーによって作成されたコードは必要ない場合に役立ちます。仕様およびその他のドキュメントでは、このようにして作成された宣言を「周囲の」状況にあるものとして参照しています。.d.ts
ファイル内のすべての宣言は「アンビエント」であることに注意することが重要です(declare
宣言タイプに応じて、明示的な修飾子を必要とするか、暗黙的に修飾するか)。
「インライン化」
パフォーマンスとコードサイズの理由から、多くの場合、コンパイル時にenumメンバーへの参照を同等の数値で置き換えることが推奨されます。
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
仕様ではこの置換と呼ばれていますが、音が涼しいのでインライン化と呼びます。たとえば、enum値がAPIの将来のバージョンで変更される可能性があるため、enumメンバーをインライン化したくない場合があります。
列挙型、それらはどのように機能しますか?
列挙型の各側面でこれを分解してみましょう。残念ながら、これらの4つのセクションはそれぞれ、他のすべての用語を参照するため、この全体を何度も読む必要があるでしょう。
計算済みvs計算なし(定数)
列挙型メンバーは、計算される場合とされない場合があります。仕様では、計算されていないメンバーは定数と呼ばれていますが、constとの混同を避けるために、それらは計算されていないメンバーと呼びます。
計算された列挙体は、その値がコンパイル時に知られていないものです。もちろん、計算されたメンバーへの参照はインライン化できません。逆に、計算されていない列挙型メンバーは、その値がコンパイル時にわかっているものです。非計算メンバーへの参照は常にインライン化されます。
計算される列挙メンバーと計算されない列挙メンバーはどれですか?まず、const
名前が示すように、列挙型のすべてのメンバーは定数です(つまり、計算されません)。非const列挙型の場合は、アンビエント(宣言)列挙型と非アンビエント列挙型のどちらを見ているかによって異なります。
a declare enum
(つまり、環境列挙型)のメンバーは、初期化子がある場合に限り、定数です。それ以外の場合は、計算されます。declare enum
では、数値初期化子のみが許可されていることに注意してください。例:
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
最後に、非宣言および非定数の列挙型のメンバーは常に計算されたと見なされます。ただし、初期化式は、コンパイル時に計算可能な場合は定数に縮小されます。これは、非const enumメンバーがインライン化されないことを意味します(この動作はTypeScript 1.5で変更されました。下部の「TypeScriptの変更」を参照してください)
constとnon-const
const
列挙型宣言はconst
修飾子を持つことができます。列挙型である場合はconst
、すべてのメンバーへの参照がインライン化。
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const enumは、コンパイル時にルックアップオブジェクトを生成しません。このため、Foo
メンバー参照の一部として以外は、上記のコードで参照するとエラーになります。Foo
実行時にオブジェクトは存在しません。
非定数
enum宣言にconst
修飾子がない場合、そのメンバーへの参照は、メンバーが計算されていない場合にのみインライン化されます。非const、非宣言の列挙型は、ルックアップオブジェクトを生成します。
宣言(周囲)対非宣言
重要な序文はdeclare
、TypeScriptには非常に特定の意味があるということです。このオブジェクトはどこかに存在します。既存のオブジェクトを説明するためのものです。declare
実際に存在しないオブジェクトを定義するためにを使用すると、悪影響が生じる可能性があります。これらについては後で説明します。
宣言する
Aは、declare enum
検索対象を放出しません。それらのメンバーが計算される場合、そのメンバーへの参照はインライン化されます(計算されたものと計算されていないものについては上記を参照)。
これは、参照のこと他の形式に注意することが重要ですdeclare enum
され、例えば、このコードは許されないコンパイルエラーはなくなり、実行時に失敗します。
// Note: Assume no other file has actually created a Foo var at runtime
declare enum Foo { Bar }
var s = 'Bar';
var b = Foo[s]; // Fails
このエラーは、「コンパイラに嘘をつかない」のカテゴリに分類されます。Foo
実行時に名前が付けられたオブジェクトがない場合は、記述しないでくださいdeclare enum Foo
。
Aはdeclare const enum
異ならないconst enum
(下記参照)--preserveConstEnumsの場合を除き、。
宣言しない
非宣言列挙は、そうでない場合、ルックアップオブジェクトを生成しますconst
。インライン化については上で説明しています。
--preserveConstEnumsフラグ
このフラグの効果は1つだけです。非宣言のconst enumはルックアップオブジェクトを生成します。インライン化は影響を受けません。これはデバッグに役立ちます。
一般的なエラー
最も一般的な間違いはdeclare enum
、通常enum
またはconst enum
より適切な場合にa を使用することです。一般的な形式は次のとおりです。
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]);
}
黄金律を覚えておいてください:実際に存在しないものは決してdeclare
ありません。使用const enum
あなたは常にインライン化したい場合、またはenum
あなたが検索対象にしたい場合。
TypeScriptの変更
TypeScript 1.4と1.5の間で、動作が変更され(https://github.com/Microsoft/TypeScript/issues/2183を参照)、宣言されていない非const列挙型のすべてのメンバーが計算されたものとして扱われるようになりました。それらはリテラルで明示的に初期化されます。これは、いわば「赤ちゃんを分割しない」ことであり、インライン動作をより予測可能にし、の概念をconst enum
通常のからより明確に分離しenum
ます。この変更以前は、非const enumの非計算メンバーはより積極的にインライン化されていました。