🦍

TypeScriptのenum型は、積極的に使わないほうがよい

2022/10/21に公開

ある日レビューをしていたところ、こんなソースコードがありました。

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を使うのは非推奨のようですね。
https://qiita.com/saba_can00/items/696baa5337eb10c37342

代替案

オブジェクトリテラルを使う。
オブジェクトのバリューのユニオン型を作れば、型安全は保てそうですね。

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を入れてみるとエラーが出ます。
ほかにも、代替案はあるそうなので、いろいろ試してみてください。
https://typescriptbook.jp/reference/values-types-variables/enum/enum-problems-and-alternatives-to-enums

※追記
ESLintで縛れるらしい。

   rules: {
        // Enum の禁止
        "no-restricted-syntax": [
            "error",
            { message: "Don't declare enums", selector: "TSEnumDeclaration" },
        ],
    },

Discussion