enum型よりunion型を
2022年1月10日
はじめに
最近TypeScriptのenum型が読みづらく、どうにかならないかと思うことが多々ありました、、
実際TypeScriptプログラマーは最近enum型よりUnion型を好む傾向にあるそうです。
enum型とは
enum Genders {
male = "MALE",
female = "FEMALE",
notSpecified = "NOT_SPECIFIED"
}
const gender: Genders = Genders.male; // "MALE"
Union型だとどうなるか
type Genders = "MALE" | "FEMALE" | "NOT_SPECIFIED"
const gender: Genders = "MALE" // MALE
純粋にこちらの方が読みやすい。
しかも型補完もしっかりと出てくれます!
ただこれだけではenum型を使わない理由になりません。
なぜenum型はつかはない方がよいのか?
数値のenumの場合は型安全にならない
enum Genders {
male, // 0
female, // 1
notSpecified // 2
}
const gender: Genders = Genders.male; // OK
const gender2: Genders = 0; // OK???????
このように予期せぬ値が入る可能性があります。
その他にもenumはTypeScriptの”a typed superset of JavaScript”に反するといったこともあるそうです。
下記の記事に非常に詳しく書かれておりました!
https://www.kabuku.co.jp/developers/good-bye-typescript-enum
まーようするに冗長な書き方になる可能性があるのと、型安全でなくなる可能性がある為、Union型を使っておいた方が無難ということになります。
const assertionを使うとenumを使わずにUnion型で代用できる
export const genders = {
male: "MALE",
female: "FEMALE",
notSpecified: "NOT_SPECIFIED"
} as const
const でgendersを普通に定義します。
console.log(genders.male) // "MALE"
as const をつけることで
readonlyがつき、型安全になっていることがわかると思います。
つまり定数になっているってことですね。
ここからUnion型を生成できます。
export type Genders = typeof genders[keyof typeof genders]; // "MALE" | "FEMALE" | "NOT_SPECIFIED"
これでkeyとvalueを定義しつつ、安全にUnion型を生成することができます。
上記がどういった挙動になっているかというと、
const Options = {
a: "AA",
b: "BB",
c: "CC",
} as const;
// 以下の2つは同じ意味。型は"AA"として解決される。
type A1 = typeof Options.a;
type A2 = typeof Options["a"];
// 添字にはUNION型が使える。型は"AA"|"BB"として解決される。
type B = typeof Options["a" | "b"];
// オブジェクトのフィールド名をUNION型として取得する。型は"a"|"b"|"c"として解決される。
type C = keyof typeof Options;
// BとCの方法を組み合わせたもの。型は"AA"|"BB"|"CC"として解決される。
type D = typeof Options[keyof typeof Options];
このようにkeyofとtypeofが使われております。
labelなどで性別を表示したい場合はenum同様switch分を使って表現することができます。
// - 型定義
import { genders, Genders } from "../types/User";
export const formatterStrings = {
gender: function (gender: Genders) {
switch (gender) {
case genders.male: return "男";
case genders.female: return "女";
case genders.notSpecified: return "指定しない";
}
}
};
return Object.values(genders).map((value: Genders) => ({
label: formatterStrings.gender(value),
value
}))
お疲れさまでした!