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 サービスを一括で開始および停止する

推薦する

macOS SierraにApache2.4+PHP7.0+MySQL5.7.16をインストールする

Mac システムには PHP と Apache が付属していますが、必要なバージョンではない場合があ...

nginx をコンパイルしてインストールした後、スムーズに nginx をアップグレードする方法

nginx をコンパイルしてインストールし、一定期間使用した後、現在のバージョンに脆弱性があることや...

Linuxで大きなファイルを素早くコピーする方法

データをコピーリモートでデータをコピーする場合、通常は rsync コマンドを使用しますが、小さなフ...

Vueコンポーネント通信方法事例まとめ

目次1. 親コンポーネントが子コンポーネントに値を渡す(props) 2. サブコンポーネントは親コ...

MySQL での GROUP_CONCAT の使用例の分析

この記事では、例を使用して、MySQL で GROUP_CONCAT を使用する方法について説明しま...

Ubuntu 18.04 に mysql5.7.23 をインストールするチュートリアル

この記事では、Ubuntu18.04にmysql5.7.23をインストールする具体的な方法を参考まで...

MySQLの連結関数CONCATの使い方の詳しい説明

前回の記事では、MySQL の置換関数 (Replace) とセグメンテーション関数 (SubStr...

Linux環境にRedisをデプロイし、Dockerにインストールする方法

インストール手順1. Redisをインストールするdocker search redis和docke...

Nginx のステータス監視とログ分析の詳細な説明

1. Nginx ステータス監視Nginx には、Nginx の全体的なアクセス ステータスを監視す...

(MariaDB) MySQL のデータ型とストレージメカニズムの包括的な説明

1.1 データ型の概要データ型は、各フィールドに保存できるデータの種類、保存できるデータの量、保存で...

MySQLからElasticsearchにデータを同期する方法の詳細な説明

目次1. 同期の原理2. ログスタッシュ入力JDBC 3. go-mysql-elasticsear...

Dockerはポートを介してコンテナに接続します

Dockerコンテナ接続1. ネットワークポートマッピングPythonアプリケーション用のコンテナを...

MySQL エラー コード 1862 の解決方法: パスワードの有効期限が切れています

ブロガーは 1 ~ 2 か月間 MySQL を使用していませんでしたが、今日この問題に遭遇しました。...

HTMLウェブページのMETAタグのコンテンツを書く際のポイント

META タグは、HTML 言語のヘッダー領域にある補助タグです。作成者、日時、Web ページの説明...

Reactのヒントはフックの依存関係の問題を解消する方法を教えます

reactプロジェクトで非常に一般的なシナリオ: const [watchValue、setWatc...