TypeScript の Enum が問題となる理由

TypeScript の Enum が問題となる理由

TypeScript は、クラス (現在は JavaScript の一部)、インターフェース、ジェネリック、ユニオン型など、静的にコンパイルされた言語の多くの機能を導入します。

しかし、今日は詳細に議論する必要がある型があります。それは enum です。

多くの静的言語では、列挙は非常に一般的な言語機能です。たとえば、C、C#、Java、Swift などです。列挙型は、コード内で使用できる定数のセットです。

TypeScript を使用して、曜日を表す新しい列挙型を作成します。

列挙型曜日 {
  日曜日、
  月曜日、
  火曜日、
  水曜日、
  木曜日、
  金曜日、
  土曜日
};

この列挙は、enum キーワードとそれに続く DayOfWeek 名を使用して宣言されます。次に、列挙で使用できる定数を定義します。

ここで、この列挙型のパラメータを受け入れて、渡されたパラメータが週末であるかどうかを判断するメソッドを定義します。

関数 isItTheWeekend(day: DayOfWeek) {
  スイッチ(日){
    DayOfWeek.Sundayの場合:
    DayOfWeek.Saturdayの場合:
      true を返します。
 
    デフォルト:
      false を返します。
  }
}

最後に、これを使用できます。

console.log(isItTheWeekend(DayOfWeek.Monday)); // ログ: false

これは、プログラム内の魔法の文字列を排除するのに非常に便利な方法です。

しかし、物事は私たちが考えるほど単純ではありません。次のコード呼び出しは、TypeScript のコンパイル後に何を取得しますか?

console.log(isItTheWeekend(2)); // これは有効ですか?

その結果を知れば、あなたはショックを受けるでしょう。このような呼び出しは TypeScript の規則に準拠しており、コンパイラは正常にコンパイルします。

どうしたの?

上記の状況から、TypeScript のバグを発見したと思われるかもしれません。実際、TypeScript はこのように設計されています。

ここで新しい数値列挙を作成し、TypeScript プレイグラウンドでコンパイルされた結果を確認できます。

曜日を指定します。
(関数 (曜日) {
    DayOfWeek[DayOfWeek["日曜日"] = 0] = "日曜日";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "月曜日";
    DayOfWeek[DayOfWeek["火曜日"] = 2] = "火曜日";
    DayOfWeek[DayOfWeek["水曜日"] = 3] = "水曜日";
    DayOfWeek[DayOfWeek["木曜日"] = 4] = "木曜日";
    DayOfWeek[DayOfWeek["金曜日"] = 5] = "金曜日";
    DayOfWeek[DayOfWeek["土曜日"] = 6] = "土曜日";
})(曜日 || (曜日 = {}));

結果は次のとおりです。

実際、列挙は JavaScript オブジェクトです。

このオブジェクトのプロパティは、定義した列挙定数に従って生成され、対応する数値は定義された順序に従って生成されます (順序としては、日曜日は 0、土曜日は 6)。このオブジェクトには、キーとして数値を持ち、値として対応する定数文字列を持つプロパティもあります。

したがって、上記のメソッドに数値を渡すことができ、その数値は対応する列挙値にマッピングされます。列挙型は数値定数と文字列定数の両方です。

いつ使うか

メソッドが列挙型のパラメータを受け取った場合でも、任意の数がコンパイルされます。このような結果は、TypeScript によって構築された型安全性システムを明らかに破壊します。これはいつ使用できますか?

JSON 文字列を返すサービスがあり、特定のプロパティが列挙型になるようにこの文字列をモデル化したいとします。

データベースに保存するのは数値です。これは、TypeScript 列挙型を定義することで簡単に解決できます。

定数日: DayOfWeek = 3;

代入中に実行されるこの明示的な型変換により、数値が列挙体の対応する定数に変換されます。つまり、コード内でこの列挙体を使用すると、コードが読みやすくなります。

列挙の数を制御する

列挙体のメンバーに対応する番号は、列挙定数によって定義された順序に従って生成されます。この数値を制御できますか?はい、できます。

列挙型ファイル状態{
  読み取り = 1、
  書く = 2
}

ファイルの可能な状態を記述する単なる列挙です。

読み取り状態または書き込み状態のいずれかにすることができ、列挙値を明示的に定義します。明示的に定義されているため、どの値が妥当であるかが明確になりました。

ビット値

しかし、ビット値という非常に便利なケースがもう 1 つあります。

FileState 列挙をもう一度見て、新しい列挙値 ReadWrite を追加してみましょう。

列挙型ファイル状態{
  読み取り = 1、
  書き込み = 2、
  読み取り書き込み = 3
}

次に、このタイプのパラメータを受け入れるメソッドがあるとします。

const file = await getFile("/path/to/file", FileState.Read | FileState.Write);

FileState で | 演算子を使用しました。この方法では、ビット演算を使用して新しい列挙値を取得できます。この例では、ReadWrite の値は 3 です。

実際、もっと明確に書くこともできます。

列挙型ファイル状態{
  読み取り = 1、
  書き込み = 2、
  ReadWrite = 読み取り | 書き込み
}

この ReadWrite の値はハードコードされているのではなく、ビット演算によって取得されます。

ただし、このような列挙型を使用する場合は注意してください。

次の列挙:

列挙型Foo{
  A = 1、
  B = 2、
  C = 3、
  D = 4、
  E = 5
}

E (または 5) を取得したい場合、ビット演算 (Foo.A | Foo.D または Foo.B | Foo.C) で実行できますか?

したがって、列挙値を使用してビット演算を実行する場合は、値を取得する方法を知っておく必要があります。

コントロールインデックス

通常、各列挙値にはデフォルトの数値があります。必要に応じて、これらの列挙値に明示的に値を割り当てることもできます。さらに、列挙体の特定の部分に値を割り当てることもできます。

列挙型曜日 {
  日曜日、
  月曜日、
  火曜日、
  水曜日 = 10、
  木曜日、
  金曜日、
  土曜日
}

最初のいくつかの値は、日曜日から火曜日まで 0 から 2 までの位置によって割り当てられます。その後、水曜日に新しい値が割り当てられ、それ以降は各値が 1 ずつ増加します。これにより問題が発生する可能性があります。

列挙型曜日 {
  日曜日、
  月曜日、
  火曜日、
  水曜日 = 10、
  木曜日 = 2、
  金曜日、
  土曜日
}

火曜日には値 2 が割り当てられます。生成された JavaScript はどのようになるでしょうか?

曜日を指定します。
(関数 (曜日) {
    DayOfWeek[DayOfWeek["日曜日"] = 0] = "日曜日";
    DayOfWeek[DayOfWeek["Monday"] = 1] = "月曜日";
    DayOfWeek[DayOfWeek["火曜日"] = 2] = "火曜日";
    DayOfWeek[DayOfWeek["水曜日"] = 10] = "水曜日";
    DayOfWeek[DayOfWeek["木曜日"] = 2] = "木曜日";
    DayOfWeek[DayOfWeek["金曜日"] = 3] = "金曜日";
    DayOfWeek[DayOfWeek["土曜日"] = 4] = "土曜日";
})(曜日 || (曜日 = {}));

火曜日と木曜日は両方とも値が 2 のようです。

そのため、設定した値を表示する必要があります。

非数値列挙

これまでは数値列挙についてのみ説明してきましたが、列挙値は必ずしも数値である必要はありません。任意の定数または計算値にすることもできます。

列挙型曜日 {
  日曜日 = 「太陽」、
  月曜日 = "Mon"、
  火曜日 = 「火曜日」、
  水曜日 = "Wed",
  木曜日 = "木"、
  金曜日 = "Fri",
  土曜日 = "Sat"
}

現在、isItTheWeekend メソッドに数値パラメータを渡すことはできません。この列挙はもはや数値列挙ではありません。ただし、列挙型ではどの値が適切であるかがわかっているため、任意の文字列を渡すことはできません。

これにより、別の問題も発生します。

定数日: DayOfWeek = "月";

これはうまくいきません。

文字列を列挙型に直接割り当てることはできませんが、明示的な型変換が必要です。

const day = "Mon" を DayOfWeek として指定します。
他の値を割り当てることはできますか?実際、列挙型には多くの種類の値があります。

enum 混乱する {
  あ、
  B = 1、
  C = 1 << 8、
  D = 1 + 2、
  E = "Hello World".長さ
}

この例の列挙値はすべて数値です。ただし、これらの数値は直接割り当てることも、計算された値や文字列の長さプロパティに割り当てることもできます。すべて定数の場合、さまざまなタイプの値を取ることができます。

列挙型MoreConfusion{
  あ、
  B = 2、
  C = "C"
}

この場合、列挙の背後にあるデータがどのように機能するかを理解するのは難しいです。したがって、このような列挙は使用しない方がよいでしょう。

結論は

TypeScript の列挙型は JavaScript を補完する優れたものであり、適切に使用すると非常に便利です。コード内に存在するマジック値の文字列と数字をクリーンアップするのに役立ちます。そして型安全です。

TypeScript Enum に問題がある理由についてはこれで終わりです。TypeScript Enum の詳細については、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • React+TypeScriptプロジェクト構築事例解説
  • TypeScript 関数の定義と使用例のチュートリアル
  • 1つの記事でTypeScriptのデータ型について学ぶ
  • Typescript の as、疑問符、感嘆符の詳細な説明
  • webpackを使用してTypeScriptコードをパッケージ化およびコンパイルする方法を教えます
  • Typescript での infer キーワードの使用に関する詳細な理解
  • TypeScript をインストール、使用、自動コンパイルする方法に関するチュートリアル
  • TypeScript インターフェース定義ケースチュートリアル

<<:  CentOS サーバーのセキュリティ構成戦略

>>:  シェルスクリプトを使用して Docker サービスを一括で開始および停止する

推薦する

html2canvas を使用して、Baidu マップを含む Dom 要素を画像に処理するソリューション

問題 1: Baidu Map はタイル画像 (地図が写真で構成されている) を使用しています。ht...

Pythonは出力をcsv操作に書き込む

以下のように表示されます。 def test_write(self): フィールド=[] field...

CSS3で実装された炎のアニメーション

成果を達成する実装コードhtml <div class="コンテナ">...

MySQL で中国語の文字をピンインでソートする簡単な例

名前を格納するフィールドが GBK 文字セットを使用している場合、GBK 内部コード自体がエンコード...

進捗バー効果を実現するJavaScript

この記事では、プログレスバー効果を実現するためのJavaScriptの具体的なコードを参考までに紹介...

JavaScript で支払いの 10 秒カウントダウンを実現

この記事では、支払いの10秒カウントダウンを実現するためのJavaScriptの具体的なコードを参考...

MySql COALESCE 関数の使用コード例

COALESCE は、各パラメータ式 (expression_1、expression_2、...、...

JavaScriptはクリックトグル機能を実装します

この記事の例では、クリックして切り替える機能を実装するためのJavaScriptの具体的なコードを参...

MySQLでJSONフィールドを操作する方法

MySQL 5.7.8 では json フィールドが導入されました。このタイプのフィールドは使用頻度...

よくあるNginxの設定ミスの例

目次ルートの場所が見つかりませんオフバイスラッシュ安全でない変数の使用スクリプト名$uri を使用す...

テーブルを作成するための MySQL SQL ステートメントの詳細な概要

mysql テーブル作成 SQL ステートメントMySQL テーブルを作成するための一般的な SQL...

ウェブページ上の小さなスペースに大きな画像を配置する方法

出典: www.bamagazine.comウェブページのバナー、ニュースの見出しの周りのスペース、...

Mac 向け MySQL のインストールと設定のチュートリアル

この記事では、MacでのMySQLインストールチュートリアルを参考までに紹介します。具体的な内容は次...

Linux環境にDocker環境をインストールする(落とし穴なし)

目次インストールの前提条件ステップ1: システムの残りを確認してクリアし、Dockerの依存関係をイ...

LeetCode の SQL 実装 (183. 注文をしたことがない顧客)

[LeetCode] 183.注文しない顧客Web サイトに、Customers テーブルと Or...