[解決済み] TypeScriptでenumのバリアントはどのように動作するのですか?
質問
TypeScriptには、enumを定義するためのさまざまな方法があります。
enum Alpha { X, Y, Z }
const enum Beta { X, Y, Z }
declare enum Gamma { X, Y, Z }
declare const enum Delta { X, Y, Z }
の値を使おうとすると
Gamma
の値を使おうとすると、エラーが発生します。
Gamma
が定義されていないためにエラーが発生しますが、これは
Delta
または
Alpha
? はどのようなものですか?
const
または
declare
というのは、ここでの宣言のことでしょうか?
もありますし
preserveConstEnums
というコンパイラフラグがありますが、これとどのような関係があるのでしょうか?
どのように解決するのですか?
TypeScriptのenumには4つの側面があり、注意する必要があります。まず、いくつかの定義です。
lookup object"。
このenumを書くと
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"
または
"Y"
.
"declare"。 または" アンビエント "です。
TypeScriptでは、コンパイラが知るべきものでありながら、実際にはコードを出力しないものを"declare"することができます。これは、jQueryのような、あるオブジェクトを定義するライブラリ(例えば
$
など)を定義しているjQueryのようなライブラリで、型情報は欲しいがコンパイラが作成するコードは必要ない場合に有効だ。仕様書や他の文書では、この方法で宣言されたものをアンビエントコンテキストと呼んでいます。
.d.ts
ファイル内のすべての宣言は "ambient"であることに注意してください (明示的な
declare
修飾子を必要とするか、暗黙的に持つか、宣言の種類によって異なります)。
"インライン化"。
パフォーマンスとコードサイズの理由から、コンパイル時にenumメンバーへの参照をその数値に置き換えることが望ましいことがよくあります。
enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";
仕様書では、これを 置換 と呼びますが、私は インライン化 と呼ぶことにします。時々、あなたは ではなく 例えば、enum の値は将来の API のバージョンで変更される可能性があるため、enum のメンバをインライン化したくない場合があります。
列挙型はどのように機能するのか?
列挙型の各側面について説明します。残念ながら、この4つのセクションはそれぞれ他のセクションの用語を参照しているので、おそらくこの全体を何度も読む必要があるでしょう。
計算されたものとそうでないもの (定数)
Enumのメンバは、以下のいずれかになります。 計算される またはそうでないものがあります。仕様では、計算されないメンバを 定数 と呼んでいますが、ここでは 非計算 との混同を避けるために const .
A 計算された enum メンバはコンパイル時にその値がわからないものです。もちろん、計算メンバへの参照はインライン化できません。逆に 非計算 enum メンバは、その値が が がコンパイル時に分かっている。計算されないメンバへの参照は常にインライン化されます。
どのenumのメンバが計算され、どれが計算されないのでしょうか?まず
const
enum の全てのメンバは、その名前が示すように定数 (すなわち、非計算) です。定数でない列挙型の場合、それは
アンビエント
(宣言) enum を見ているか、非 ambient enum を見ているかによります。
のメンバは
declare enum
(すなわちアンビエントな列挙型) は、定数
の場合のみ
の場合のみ定数であり、イニシャライザを持ちます。それ以外の場合は、計算されます。注意点として
declare enum
では、数値のイニシャライザのみが許可されることに注意してください。例
declare enum Foo {
X, // Computed
Y = 2, // Non-computed
Z, // Computed! Not 3! Careful!
Q = 1 + 1 // Error
}
最後に、non-declare nononst enums のメンバは常に計算されるものとみなされます。しかし、コンパイル時に計算可能であれば、その初期化式は定数に還元される。つまり、non-const enumのメンバは決してインライン化されない(この動作はTypeScript 1.5で変更されたため、最下部の"TypeScriptの変更点"を参照)。
constとnon-const
定数
enum宣言には
const
修飾子があります。もしenumが
const
,
すべて
への参照はインライン化されています。
const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always
const enum はコンパイル時にルックアップ・オブジェクトを生成しません。このため、定数型列挙体を参照することはエラーとなります。
Foo
を参照することはエラーです。いいえ
Foo
オブジェクトは実行時に存在しません。
非制約
enum宣言に
const
修飾子がない場合、そのメンバへの参照はメンバが非計算の場合のみインライン化されます。非制約、非宣言のenumはルックアップ・オブジェクトを生成します。
declare (ambient) vs non-declare(アンビエント宣言)。
重要な前置きとして
declare
は非常に特殊な意味を持つということです。
このオブジェクトは他の場所に存在する
. を記述するためのものです。
存在する
オブジェクトを記述するためのものです。使用方法
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 の場合を除きます(下記参照)。
非宣言
non-declare enum は、ルックアップ・オブジェクトを生成する場合、それが
const
. インライン化については前述の通りです。
--preserveConstEnums フラグ
このフラグの効果はただ一つ: 非宣言の const enums はルックアップ・オブジェクトを生成します。インライン化は影響を受けません。これはデバッグに便利です。
よくあるエラー
最もよくある間違いは
declare enum
を使うことです。
enum
または
const enum
の方がより適切でしょう。よくある形はこうです。
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
を参照)、リテラルで明示的に初期化されていても、宣言されていないnon-const enumのメンバーはすべてcomputedとして扱われるようになった。これはいわば、インライン化の挙動をより予測しやすくし、よりきれいに
const enum
と通常の
enum
. この変更以前は、非構成式列挙型の計算されないメンバはより積極的にインライン化されていました。
関連
-
[解決済み] Godotで文字列をenumに変換するには?
-
[解決済み] enumを列挙するには
-
[解決済み] intをenumにキャストするにはどうすればよいですか?
-
[解決済み] Javaで文字列値からenum値を取得する方法
-
[解決済み] C#の[Flags]Enum属性の意味とは?
-
[解決済み] TypeScriptで文字列を数値に変換する方法とは?
-
[解決済み] Pythonで'Enum'を表現するにはどうしたらいいですか?
-
[解決済み] TypeScript で `window` に新しいプロパティを明示的に設定するにはどうすればよいですか?
-
[解決済み] TypeScriptで文字列をenumに変換するには?
-
[解決済み] enumの項目名を取得する方法は?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン