陣営とか派閥という意味合いのEnumを定義したかった
export enum Faction {
PLAYER,
ENEMY,
ALLY
};
しかし様々な理由によってTypeScriptではEnumを避けることが推奨されていて
以下のようにオブジェクトリテラルを用いた代替手段が紹介されていた。
export const Factions = {
PLAYER : 0,
ENEMY : 1,
ALLY : 2,
} as const;
export type Faction = typeof Factions[keyof typeof Factions];
やりたい事ができればどちらでもいいのだが
このオブジェクトリテラルで書いてる呪文が何やってるのかよくわかんねぇ...
と思ったのでこのあたりを解読していきたい。
最終的にどうなるのかを把握しておく
まず最初にスタートとゴールを確認しておこう
以下の怪しげな呪文の部分
export type Faction = typeof Factions[keyof typeof Factions];
これは最終的に以下のただのUnion型になる
実際0,1,2しか入らない型が欲しいわけだから問題ない、OK
type Faction = 0 | 1 | 2;
問題はどういう流れで、どういう理屈で
最終的にこのユニオン型になるのか
これがよくわかっていない、とりあえずこう書けばいいらしい状態である。
1行で書かずに複数行に分けて書けば普通のことだった
変換のロジックを1つずつ紐解いていく
// keyof typeof Factionsって書くと要するにこうなる
type keys = "PLAYER" | "ENEMY" | "ALLY";
// type Faction = typeof Factions[keyof typeof Factions];って書いてるのと同じ
type Faction = typeof Factions[keys];
直値で書くのを辞める
type keys = keyof typeof Factions; // => "PLAYER" | "ENEMY" | "ALLY"
type Faction = typeof Factions[keys] // => typeof Factions["PLAYER" | "ENEMY" | "ALLY"]
最終的に1行で書くとこうなるだけ
type Faction = typeof Factions[keyof typeof Factions];
1行にまとまってたからなんかよくわからねぇ...
ってなっていただけで分割して考えればどうということはなかった...
ちな、as constを付ける必要性
export const Factions = {
PLAYER : 0,
ENEMY : 1,
ALLY : 2,
};
もしこのようにas constをつけなかったら
Factions.PLAYER = 10;
こんな風に値を書き換えることができるので
Factions.PLAYERが0となる保証はない。
つまりFactionsの値はただの数値なので型はこうなる
const Factions: {
PLAYER : number,
ENEMY : number,
ALLY : number,
}
値がnumberなので、当然このように型を定義すれば
type Faction = typeof Factions[keyof typeof Factions];
Faction型はこうなる
type Faction = number;
オブジェクトにas constをつけて、値が不変であることを保証しないと
ただのnumber型になってしまうのは順を追って見れば当然のことだとわかる。
おわり
最近はこうかけばいいよーってHowは書かれてるけど
なぜそう書けばよくなるのかのWhyがなくてしんどいおじさん
以上