🦍
TypeScriptのenum型は、積極的に使わないほうがよい
ある日レビューをしていたところ、こんなソースコードがありました。
const Sample = () => {
const [gender, setGender] = useState<number>(0);
return (
<div>
<input
type="radio"
checked={gender === 0}
onChange={() => setGender(0)}
/>
男性
<input
type="radio"
checked={gender === 1}
onChange={() => setGender(1)}
/>
女性
<input
type="radio"
checked={gender === 2}
onChange={() => setGender(2)}
/>
全て
</div>
);
};
わからんでもないけど読みづらいですね。型も安全とは言い切れないです。(0,1,2のみ許可したいところですね)
一年前にC言語を書いていたかすかな記憶から、enumってあったなとおもい、
レビューでこう返しました。
enum Gender {
MALE,
FEMALE,
ALL,
}
const Sample = () => {
const [gender, setGender] = useState<Gender>(0);
return (
<div>
<input
type="radio"
checked={gender === Gender.MALE}
onChange={() => setGender(Gender.MALE)}
/>
男性
<input
type="radio"
checked={gender === Gender.FEMALE}
onChange={() => setGender(Gender.FEMALE)}
/>
女性
<input
type="radio"
checked={gender === Gender.ALL}
onChange={() => setGender(Gender.ALL)}
/>
全て
</div>
);
};
enumは型安全ではない?
一件落着と思いきや、これ、
「0,1,2以外も入ってしまうんですけど大丈夫ですか?」
とのチャットが
enum Gender {
MALE = 0,
FEMALE = 1,
ALL = 2,
}
const human: Gender = 10;
確かに、エラー出ないですね。enumではnumber型はすべて許可されてしまいます。
上記(+その他さまざまな)理由から、TypeScriptでenumを使うのは非推奨のようですね。
代替案
オブジェクトリテラルを使う。
オブジェクトのバリューのユニオン型を作れば、型安全は保てそうですね。
const Gender = {
MALE: 0,
FEMALE: 1,
ALL: 2,
} as const;
type GenderType = typeof Gender[keyof typeof Gender];
const Sample = () => {
const [gender, setGender] = useState<GenderType>(0);
return (
<div>
<input
type="radio"
checked={gender === Gender.MALE}
onChange={() => setGender(Gender.MALE)}
/>
男性
<input
type="radio"
checked={gender === Gender.FEMALE}
onChange={() => setGender(Gender.FEMALE)}
/>
女性
<input
type="radio"
checked={gender === Gender.ALL}
// エラー
onChange={() => setGender(4)}
/>
全て
</div>
);
};
試しに4を入れてみるとエラーが出ます。
ほかにも、代替案はあるそうなので、いろいろ試してみてください。
※追記
ESLintで縛れるらしい。
rules: {
// Enum の禁止
"no-restricted-syntax": [
"error",
{ message: "Don't declare enums", selector: "TSEnumDeclaration" },
],
},
Discussion