🫠

TypeScriptでなぜenumを使うべきではないのかを調べた

2023/06/17に公開

TypeScriptでは、よくenumを使うのはやめましょう!とよく聞く。
自分も、なんとなくそんな感じでenumを使うことを避けていたが、改めてなぜenumを使うべきではないのかを今さら調べたので、その内容をまとめたいと思います。

// bad👎
enum HttpStatusCode {
  OK = 200,
  NOT_FOUND = 404,
  INTERNAL_SERVER_ERROR = 500
}

// good👍
const CODES = {
  OK: 200,
  NOT_FOUND: 404,
  INTERNAL_SERVER_ERROR: 500,
} as const

type HttpStatusCode = typeof CODES [keyof typeof CODES]

課題

数値の場合だと型安全ではない

まず、文字列の列挙型に出力した場合に、どのような挙動をするか確認したいと思います。

enum Season {
  Spring = "spring",
  Summer = "summer",
  Autumn = "autumn",
  Winter = "winter"
}
console.log(Season)
// { Spring: "spring", Summer: "summer", Autumn: "autumn", Winter: "winter" }

const season: Season = "foo" // Type '"foo"' is not assignable to type 'Season'.

もちろんのことですが、Season列挙型の変数にfooを代入しようとすると、エラーとなります。
では、数値の場合にはどのような挙動をするでしょう?

enum Numbers {
  One = 1,
  Two = 2,
  Three = 3
}
console.log(Numbers)
// { 1: "One", 2: "Two", 3: "Three", One: 1, Two: 2, Three: 3 }

const number: Numbers = 4 // ✅

文字列の場合と異なり、Numbers列挙型には数字をキーとした要素も含まれています。
また、Numbers列挙型の変数に対して、想定外の値を代入したとしてもエラーにならない問題があります。
このような型の安全性が保証されていないために、enumを使うべきではないと言われているかと思います。

文字列列挙型だけ公称型になる

公称型は、こちらの記事が非常に分かりやすいです。
つまり、プロパティの構造が一致(構造的部分型)していたとしても、変数の継承関係が保証(公称型)されていないとダメということですね。

// 数字の場合
enum Numbers {
  One = 1,
  Two = 2,
  Three = 3
}

const number: Numbers = 1 // ✅

// 文字列の場合
enum Season {
  Spring = "spring",
  Summer = "summer",
  Autumn = "autumn",
  Winter = "winter"
}

const spring: Season = Season.Spring // ✅
const summer: Season = "summer" // Type '"summer"' is not assignable to type 'Season'.

参考文献

Discussion