TypeScript では、さまざまな列挙型がどのように機能しますか? 質問する

TypeScript では、さまざまな列挙型がどのように機能しますか? 質問する

TypeScript には列挙型を定義するさまざまな方法があります。

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の場合はそうではありません。ここでの宣言におけるまたは はどういう意味ですか?DeltaAlphaconstdeclare

コンパイラ フラグもありますがpreserveConstEnums、これはこれらとどのように相互作用するのでしょうか?

ベストアンサー1

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""Y"

"宣言する"または "周囲

TypeScript では、コンパイラが認識する必要があるが、実際にはコードを出力しないものを「宣言」できます。これは、$型情報が必要なオブジェクト (例 ) を定義する jQuery などのライブラリがあり、コンパイラによってコードが作成される必要がない場合に便利です。仕様およびその他のドキュメントでは、このようにして行われた宣言を「アンビエント」コンテキストにあると呼んでいます。ファイル内のすべての宣言が.d.ts「アンビエント」である (declare宣言タイプに応じて、明示的な修飾子を必要とするか、暗黙的に修飾子を持つ) ことに注意することが重要です。

「インライン化」

パフォーマンスとコード サイズの理由から、コンパイル時に enum メンバーへの参照をそれに相当する数値に置き換えることが望ましい場合がよくあります。

enum Foo { X = 4 }
var y = Foo.X; // emits "var y = 4";

仕様ではこれを代替私はそれを呼ぶことにするインライン化かっこいいから。ないたとえば、列挙値が API の将来のバージョンで変更される可能性があるため、列挙メンバーをインライン化する必要があります。


列挙型はどのように機能しますか?

列挙型の各側面について詳しく説明しましょう。残念ながら、これら 4 つのセクションはそれぞれ、他のすべてのセクションの用語を参照するため、全体を複​​数回読む必要があるでしょう。

計算値と非計算値(定数)

列挙型のメンバーは計算された仕様では非計算メンバーを絶え間ない、でも私は彼らを呼ぶつもりです計算されていない混同を避けるために定数

計算されたenumメンバーはコンパイル時に値が分からないメンバーです。もちろん、計算メンバーへの参照はインライン化できません。逆に、計算されていないenumメンバーは、その値がコンパイル時に認識されます。非計算メンバーへの参照は常にインライン化されます。

どの列挙型メンバーが計算型で、どのメンバーが非計算型か?まず、const列挙型のメンバーはすべて定数(つまり非計算型)であり、その名前が示す通りです。非定数列挙型の場合、周囲(宣言) enum または非アンビエント enum。

declare enum(つまりアンビエント列挙型)のメンバーは定数であるもし、もし、初期化子を持ちます。そうでない場合は計算されます。 ではdeclare enum数値初期化子のみが許可されることに注意してください。例:

declare enum Foo {
    X, // Computed
    Y = 2, // Non-computed
    Z, // Computed! Not 3! Careful!
    Q = 1 + 1 // Error
}

最後に、宣言されていない非定数列挙型のメンバーは常に計算済みとみなされます。ただし、コンパイル時に計算可能な場合は、初期化式が定数に縮小されます。つまり、非定数列挙型のメンバーはインライン化されません (この動作は TypeScript 1.5 で変更されました。下部の「TypeScript の変更点」を参照してください)。

const と非 const

定数

enum宣言にはconst修飾子を付けることができます。enumが の場合const全てインライン化されたメンバーへの参照。

const enum Foo { A = 4 }
var x = Foo.A; // emitted as "var x = 4;", always

const enums は、コンパイル時にルックアップ オブジェクトを生成しません。このため、Fooメンバー参照の一部以外として上記のコードで参照するとエラーになります。Foo実行時にはオブジェクトは存在しません。

非定数

enum 宣言に修飾子がない場合const、そのメンバーへの参照は、メンバーが非計算型の場合にのみインライン化されます。非 const 非宣言 enum は、ルックアップ オブジェクトを生成します。

宣言(アンビエント)と非宣言

重要な前置きは、declareTypeScript では非常に具体的な意味を持つということです。このオブジェクトは別の場所に存在します. 記述するためのものです既存 オブジェクト。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

--preserveConstEnums の場合を除き、A はdeclare const enuma と変わりません(以下を参照)。const enum

非宣言

宣言されていない列挙型は、そうでない場合はルックアップ オブジェクトを生成しますconst。インライン化については上記で説明しました。

--preserveConstEnums フラグ

このフラグの効果は 1 つだけです。宣言されていない const 列挙型はルックアップ オブジェクトを生成します。インライン展開は影響を受けません。これはデバッグに役立ちます。


よくあるエラー

最も一般的な間違いは、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の間で動作が変更されました(参考:) により、宣言されていない非定数列挙型のすべてのメンバーが、リテラルで明示的に初期化されている場合でも、計算済みとして扱われるようになりました。これは、いわば「赤ちゃんを分割解除」し、インライン化の動作をより予測可能にし、 の概念をconst enum通常の からより明確に分離しますenum。この変更の前は、非定数列挙型の非計算済みメンバーは、より積極的にインライン化されていました。

おすすめ記事