著者はCOVID-19救済基金を選択し、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
TypeScript では、 enums 、または列挙型は、一定の値のセットを保持する一定の長さのデータ構造です。 これらの定数値のそれぞれは、列挙型のメンバーとして知られています。 列挙型は、特定の数の可能な値のみになる可能性のあるプロパティまたは値を設定する場合に役立ちます。 一般的な例の1つは、トランプのデッキにある1枚のカードのスーツの価値です。 引かれるすべてのカードは、クラブ、ダイアモンド、ハート、またはスペードのいずれかになります。 これらの4つを超える可能性のあるスーツの値はなく、これらの可能な値は変更されない可能性があります。 このため、列挙型は、カードの可能なスーツを説明するための効率的で明確な方法になります。
TypeScriptのほとんどの機能はコンパイル中にエラーをスローするのに役立ちますが、列挙型はコードの定数を保持できるデータ構造としても役立ちます。 TypeScriptは、コンパイラーによって発行された最終コードで列挙型をJavaScriptオブジェクトに変換します。 このため、列挙型を使用してコードベースを読みやすくすることができます。これは、同じデータ構造に複数の定数値をグループ化できると同時に、異なるconst
変数を配置するよりもコードをタイプセーフにすることができるためです。その周り。
このチュートリアルでは、列挙型の作成に使用される構文、TypeScriptコンパイラが内部で作成するJavaScriptコード、列挙オブジェクトタイプを抽出する方法、およびゲーム開発でビットフラグを含む列挙の使用例について説明します。
前提条件
このチュートリアルに従うには、次のものが必要です。
- TypeScriptプログラムを実行して、例に従うことができる環境。 これをローカルマシンに設定するには、次のものが必要です。 TypeScript関連のパッケージを処理する開発環境を実行するためにインストールされたNodeとnpm(またはyarn)の両方。 このチュートリアルは、Node.jsバージョン14.3.0およびnpmバージョン6.14.5でテストされました。 macOSまたはUbuntu18.04にインストールするには、「Node.jsをインストールしてmacOSにローカル開発環境を作成する方法」または「Ubuntu18.04にNode.jsをインストールする方法」の「PPAを使用したインストール」セクションの手順に従います。 これは、Windows Subsystem for Linux(WSL)を使用している場合にも機能します。 さらに、TypeScriptコンパイラ(tsc)がマシンにインストールされている必要があります。 これを行うには、TypeScriptの公式Webサイトを参照してください。
- ローカルマシン上にTypeScript環境を作成したくない場合は、公式の TypeScriptPlaygroundを使用してフォローできます。
- JavaScript、特に destructuring、RESTパラメーター、 imports /exportsなどのES6+構文に関する十分な知識が必要です。 これらのトピックに関する詳細情報が必要な場合は、JavaScriptシリーズのコーディング方法を読むことをお勧めします。
- このチュートリアルでは、TypeScriptをサポートし、インラインエラーを表示するテキストエディタの側面を参照します。 これはTypeScriptを使用するために必要ではありませんが、TypeScript機能をさらに活用します。 これらの利点を活用するには、 Visual Studio Code のようなテキストエディターを使用できます。このエディターは、TypeScriptをすぐにサポートします。 TypeScriptPlaygroundでこれらの利点を試すこともできます。
このチュートリアルに示されているすべての例は、TypeScriptバージョン4.2.3を使用して作成されています。
TypeScriptでの列挙型の作成
このセクションでは、数値列挙型と文字列列挙型の両方を宣言する例を実行します。
TypeScriptの列挙型は通常、特定の値に対して決定された数のオプションを表すために使用されます。 このデータは、キーと値のペアのセットに配置されます。 一般的なJavaScriptオブジェクトと同様に、キーは文字列である必要がありますが、列挙型メンバーの値は、主に1つのメンバーを他のメンバーと区別するために使用される自動インクリメントの数値であることがよくあります。 数値のみの列挙型は、数値列挙型と呼ばれます。
数値列挙型を作成するには、enum
キーワードに続けて、列挙型の名前を使用します。 次に、中括弧({}
)ブロックを作成します。ここで、次のように内部の列挙型メンバーを指定します。
enum CardinalDirection {
North = 1,
East,
South,
West,
};
この例では、CardinalDirection
という列挙型を作成しています。この列挙型には、各基本方位を表すメンバーがあります。 値には常に北、南、東、西の4つのオプションしかないため、列挙型はこれらのオプションを保持するためのデータ構造の適切な選択です。
CardinalDirection
列挙型の最初のメンバーの値として数値1
を使用しました。 これにより、番号1
がNorth
の値に割り当てられます。 ただし、他のメンバーに値を割り当てていません。 これは、TypeScriptが残りのメンバーを前のメンバーの値に1を加えた値に自動的に設定するためです。 CardinalDirection.East
の値は2
、CardinalDirection.South
の値は3
、CardinalDirection.West
の値は4
になります。 ]。
この動作は、各メンバーの数値のみを持つ数値列挙型でのみ機能します。
enumメンバーの値の設定を完全に無視することもできます。
enum CardinalDirection {
North,
East,
South,
West,
};
この場合、TypeScriptは最初のメンバーを0
に設定し、次に他のメンバーをそのメンバーに基づいて自動的に設定し、それぞれを1つずつインクリメントします。 これにより、次のようなコードになります。
enum CardinalDirection {
North = 0,
East = 1,
South = 2,
West = 3,
};
TypeScriptコンパイラはデフォルトで列挙型メンバーに番号を割り当てますが、これをオーバーライドして文字列列挙型を作成できます。 これらは、各メンバーの文字列値を持つ列挙型です。 これらは、後でログまたはエラーメッセージで値を読み取る必要がある場合など、値が人間が読める特定の意味を持つ必要がある場合に役立ちます。
次のコードを使用して、列挙型メンバーが文字列値を持つように宣言できます。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W'
}
これで、各方向には、それらが関連付けられている方向を示す文字値があります。
宣言構文について説明したので、基になるJavaScriptをチェックして、キーと値のペアの双方向性など、列挙型の動作について詳しく知ることができます。
双方向列挙型メンバー
TypeScriptのコンパイル時に、列挙型はJavaScriptオブジェクトに変換されます。 ただし、列挙型には、オブジェクトと区別するためのいくつかの機能があります。 これらは、従来のJavaScriptオブジェクトよりも定数メンバーを格納するためのより安定したデータ構造を提供し、列挙型メンバーの双方向参照も提供します。 これがどのように機能するかを示すために、このセクションでは、TypeScriptが最終的なコードで列挙型をコンパイルする方法を示します。
前のセクションで作成した文字列列挙型を取得します。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
TypeScriptコンパイラを使用してJavaScriptにコンパイルすると、これは次のコードになります。
"use strict";
var CardinalDirection;
(function (CardinalDirection) {
CardinalDirection["North"] = "N";
CardinalDirection["East"] = "E";
CardinalDirection["South"] = "S";
CardinalDirection["West"] = "W";
})(CardinalDirection || (CardinalDirection = {}));
このコードでは、"use strict"
文字列は、JavaScriptのより制限的なバージョンである strictmodeを開始します。 その後、TypeScriptは値のない変数CardinalDirection
を作成します。 次に、コードには、CardinalDirection
変数を引数として取り、その値を空のオブジェクト({}
)に設定する、即時呼び出し関数式(IIFE)が含まれます。まだ設定されていません。
関数内で、CardinalDirection
が空のオブジェクトとして設定されると、コードはそのオブジェクトに複数のプロパティを割り当てます。
"use strict";
var CardinalDirection;
(function (CardinalDirection) {
CardinalDirection["North"] = "N";
CardinalDirection["East"] = "E";
CardinalDirection["South"] = "S";
CardinalDirection["West"] = "W";
})(CardinalDirection || (CardinalDirection = {}));
各プロパティは元の列挙型の1つのメンバーであり、値は列挙型のメンバー値に設定されていることに注意してください。
文字列列挙型の場合、これでプロセスは終了です。 しかし次に、前のセクションの数値列挙型で同じことを試してみます。
enum CardinalDirection {
North = 1,
East,
South,
West,
};
これにより、強調表示されたセクションが追加された次のコードが生成されます。
"use strict";
var CardinalDirection;
(function (CardinalDirection) {
CardinalDirection[CardinalDirection["North"] = 1] = "North";
CardinalDirection[CardinalDirection["East"] = 2] = "East";
CardinalDirection[CardinalDirection["South"] = 3] = "South";
CardinalDirection[CardinalDirection["West"] = 4] = "West";
})(CardinalDirection || (CardinalDirection = {}));
列挙型の各メンバーがオブジェクト(CardinalDirection["North"] = 1]
)のプロパティになることに加えて、列挙型は各数値のキーを作成し、値として文字列を割り当てます。 North
の場合、CardinalDirection["North"] = 1
は値1
を返し、CardinalDirection[1] = "North"
は値"North"
をキー[に割り当てます。 X122X]。
これにより、数値メンバーの名前とその値の間の双方向の関係が可能になります。 これをテストするには、以下をログに記録します。
console.log(CardinalDirection.North)
これにより、"North"
キーの値が返されます。
Output1
次に、次のコードを実行して、参照の方向を逆にします。
console.log(CardinalDirection[1])
出力は次のようになります。
Output"North"
列挙型を表す最終的なオブジェクトを説明するために、列挙型全体をコンソールに記録します。
console.log(CardinalDirection)
これにより、双方向効果を作成するキーと値のペアの両方のセットが表示されます。
Output{
"1": "North",
"2": "East",
"3": "South",
"4": "West",
"North": 1,
"East": 2,
"South": 3,
"West": 4
}
TypeScriptの内部で列挙型がどのように機能するかを理解したら、次に列挙型を使用してコードで型を宣言します。
TypeScriptでの列挙型の使用
このセクションでは、TypeScriptコードで列挙型メンバーを型として割り当てる基本的な構文を試します。 これは、基本タイプが宣言されているのと同じ方法で実行できます。
CardinalDirection
列挙型をTypeScriptの変数の型として使用するには、次の強調表示されたコードに示すように、列挙型名を使用できます。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
const direction: CardinalDirection = CardinalDirection.North;
型として列挙型を持つように変数を設定していることに注意してください。
const direction: CardinalDirection = CardinalDirection.North;
また、変数値を列挙型のメンバーの1つ(この場合はCardinalDirection.North
)に設定しています。 列挙型はJavaScriptオブジェクトにコンパイルされるため、これを行うことができます。したがって、列挙型は型であることに加えて値表現も持っています。
direction
変数の列挙型と互換性のない値を渡すと、次のようになります。
const direction: CardinalDirection = false;
TypeScriptコンパイラはエラー2322
を表示します。
OutputType 'false' is not assignable to type 'CardinalDirection'. (2322)
したがって、direction
は、CardinalDirection
列挙型のメンバーにのみ設定できます。
変数のタイプを特定の列挙型メンバーに設定することもできます。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
const direction: CardinalDirection.North = CardinalDirection.North;
この場合、変数はCardinalDirection
列挙型のNorth
メンバーにのみ割り当てることができます。
列挙型のメンバーに数値がある場合は、変数の値をそれらの数値に設定することもできます。 たとえば、列挙型が与えられた場合:
enum CardinalDirection {
North = 1,
East,
South,
West,
};
タイプCardinalDirection
の変数の値を1
に設定できます。
const direction: CardinalDirection = 1;
これが可能なのは、1
がCardinalDirection
列挙型のNorth
メンバーの値であるためです。 これは列挙型の数値メンバーに対してのみ機能し、コンパイルされたJavaScriptが数値列挙型メンバーに対して持つ双方向の関係に依存します。これについては前のセクションで説明しました。
列挙型を使用して変数型を宣言することを試みたので、次のセクションでは、列挙型を操作する特定の方法、つまり基になるオブジェクト型を抽出する方法を示します。
列挙型のオブジェクトタイプの抽出
前のセクションで、列挙型はJavaScriptに加えてタイプレベルの拡張機能であるだけでなく、実際の値を持っていることがわかりました。 これは、列挙型データ構造自体に型があることも意味します。これは、列挙型のインスタンスを表すJavaScriptオブジェクトを設定しようとする場合に考慮する必要があります。 これを行うには、列挙型オブジェクト表現自体のタイプを抽出する必要があります。
CardinalDirection
列挙型が与えられた場合:
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
次のように、列挙型に一致するオブジェクトを作成してみてください。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
const test1: CardinalDirection = {
North: CardinalDirection.North,
East: CardinalDirection.East,
South: CardinalDirection.South,
West: CardinalDirection.West,
}
このコードでは、test1
はタイプCardinalDirection
のオブジェクトであり、オブジェクト値には列挙型のすべてのメンバーが含まれます。 ただし、TypeScriptコンパイラはエラー2322
を表示します。
OutputType '{ North: CardinalDirection; East: CardinalDirection; South: CardinalDirection; West: CardinalDirection; }' is not assignable to type 'CardinalDirection'.
このエラーの理由は、CardinalDirection
型が、列挙型オブジェクト自体の型ではなく、すべての列挙型メンバーの共用体型を表すためです。 列挙型の名前の前にtypeof
を使用すると、オブジェクトタイプを抽出できます。 以下の強調表示されたコードを確認してください。
enum CardinalDirection {
North = 'N',
East = 'E',
South = 'S',
West = 'W',
};
const test1: typeof CardinalDirection = {
North: CardinalDirection.North,
East: CardinalDirection.East,
South: CardinalDirection.South,
West: CardinalDirection.West,
}
これで、TypeScriptコンパイラがコードを正しくコンパイルできるようになります。
このセクションでは、列挙型の使用を拡大するための具体的な方法を示しました。 次に、列挙型が適用可能なユースケース、つまりゲーム開発のビットフラグについて説明します。
TypeScript列挙型でのビットフラグの使用
チュートリアルのこの最後のセクションでは、TypeScriptの列挙型の具体的なユースケースであるビットフラグについて説明します。
ビットフラグは、ビット演算を使用して、さまざまなブール値のようなオプションを1つの変数に表す方法です。 これが機能するためには、各フラグが32ビット数の正確に1ビットを使用する必要があります。これは、ビット単位の演算を実行するときにJavaScriptで許可される最大値です。 最大32ビット数は2,147,483,647
で、バイナリでは1111111111111111111111111111111
であるため、31
の可能なフラグがあります。
ゲームを構築していると想像してください。プレーヤーは、SKILL_A
、SKILL_B
、SKILL_C
などのさまざまなスキルを持っている可能性があります。 プレーヤーが特定のスキルを持っていることをプログラムが確実に認識できるようにするために、プレーヤーのステータスに応じてオンまたはオフにできるフラグを作成できます。
次の擬似コードを使用して、各スキルフラグにバイナリ値を指定します。
SKILL_A = 0000000000000000000000000000001
SKILL_B = 0000000000000000000000000000010
SKILL_C = 0000000000000000000000000000100
ビット演算子|
( OR )を使用して、プレーヤーの現在のスキルをすべて1つの変数に格納できるようになりました。
playerSkills = SKILL_A | SKILL_B
この場合、|
演算子を使用してビットフラグ0000000000000000000000000000001
とビットフラグ0000000000000000000000000000010
をプレーヤーに割り当てると、0000000000000000000000000000011
が生成されます。両方のスキル。
さらにスキルを追加することもできます。
playerSkills |= SKILL_C
これにより、0000000000000000000000000000111
が生成され、プレーヤーが3つのスキルすべてを持っていることを示します。
ビット演算子&
( AND )と~
( NOT )の組み合わせを使用してスキルを削除することもできます。
playerSkills &= ~SKILL_C
次に、プレーヤーが特定のスキルを持っているかどうかを確認するには、ビット演算子&
( AND )を使用します。
hasSkillC = (playerSkills & SKILL_C) == SKILL_C
プレイヤーがSKILL_C
スキルを持っていない場合、(playerSkills & SKILL_C)
パーツは0
に評価されます。 それ以外の場合、(playerSkills & SKILL_C)
は、テストしているスキルの正確な値(この場合はSKILL_C
(0000000000000000000000000000010
))に評価されます。 このようにして、評価された値がテスト対象のスキルの値と同じであることをテストできます。
TypeScriptでは列挙型メンバーの値を整数に設定できるため、これらのフラグを列挙型として格納できます。
enum PlayerSkills {
SkillA = 0b0000000000000000000000000000001,
SkillB = 0b0000000000000000000000000000010,
SkillC = 0b0000000000000000000000000000100,
SkillD = 0b0000000000000000000000000001000,
};
接頭辞0b
を使用して、2進数を直接表すことができます。 このような大きなバイナリ表現を使用したくない場合は、ビット演算子<<
(左シフト)を使用できます。
enum PlayerSkills {
SkillA = 1 << 0,
SkillB = 1 << 1,
SkillC = 1 << 2,
SkillD = 1 << 3,
};
1 << 0
は、0b0000000000000000000000000000001
、1 << 1
から0b0000000000000000000000000000010
、1 << 2
から0b0000000000000000000000000000100
、および[X94Xに評価されます。 ]から0b0000000000000000000000000001000
へ。
これで、playerSkills
変数を次のように宣言できます。
let playerSkills: PlayerSkills = PlayerSkills.SkillA | PlayerSkills.SkillB;
注: playerSkills
変数のタイプをPlayerSkills
に明示的に設定する必要があります。そうしないと、TypeScriptはタイプnumber
であると推測します。
さらにスキルを追加するには、次の構文を使用します。
playerSkills |= PlayerSkills.SkillC;
スキルを削除することもできます。
playerSkills &= ~PlayerSkills.SkillC;
最後に、列挙型を使用して、プレーヤーが特定のスキルを持っているかどうかを確認できます。
const hasSkillC = (playerSkills & PlayerSkills.SkillC) === PlayerSkills.SkillC;
このソリューションは、内部でビットフラグを使用しながら、データを表示するためのより読みやすく整理された方法を提供します。 また、バイナリ値を定数として列挙型に格納し、playerSkills
変数がビットフラグと一致しない場合にエラーをスローすることで、コードをよりタイプセーフにします。
結論
列挙型は、型システムを提供するほとんどの言語で一般的なデータ構造であり、これはTypeScriptでも同じです。 このチュートリアルでは、TypeScriptで列挙型を作成して使用すると同時に、列挙型のオブジェクトタイプの抽出やビットフラグの使用など、いくつかのより高度なシナリオを実行しました。 列挙型を使用すると、コードベースを読みやすくすると同時に、定数をグローバルスペースに残すのではなく、データ構造に編成できます。
TypeScriptのその他のチュートリアルについては、TypeScriptシリーズのコーディング方法ページをご覧ください。