TypeScriptのenumを使わない
さようなら、TypeScript enum | Kabuku Developers Blog
TypeScriptのenumを使わないほうがいい理由を、Tree-shakingの観点で紹介します - LINE ENGINEERING
enumが使われているコードがあったので修正したいと思い調べた(昨日as const
を調べたのもその一環)
挙げた記事は多少流し読みしたことがあったのだけど、今回ちゃんと読んだ。
enum
そもそもまず以下の書き方を知らなかった。
// 指定しない場合は0から順番に数字が振られる
enum NumEnum {
Aaa,
Bbb,
Ccc
}
const hoge: NumEnum = NumEnum.Aaa // => 0
const fuga: NumEnum = NumEnum.Bbb // => 1
const piyo: NumEnum = NumEnum.Ccc // => 2
// const enumというのもある
const enum ConstEnum {
Aaa,
Bbb,
Ccc
}
コンパイル後のコードとか今まで全然思いを馳せてなかったので参考になった。
var NumEnum;
(function (NumEnum) {
NumEnum[NumEnum["Aaa"] = 0] = "Aaa";
NumEnum[NumEnum["Bbb"] = 1] = "Bbb";
NumEnum[NumEnum["Ccc"] = 2] = "Ccc";
})(NumEnum || (NumEnum = {}));
const hoge = NumEnum.Aaa;
const fuga = NumEnum.Bbb;
const piyo = NumEnum.Ccc;
難しいけど、即時関数の引数にNumEnum || (NumEnum = {})
という値を渡していて、これはNumEnumがfalsyなら空のオブジェクトを渡す、ということだろうか。
NumEnum[NumEnum["Aaa"] = 0] = "Aaa";
めちゃくちゃ見慣れない書き方で何やってるのか全然分からん……。 devtoolsのconsoleで見てみたらこうなってた。
> NumberEnum
// => {0: 'Foo', 1: 'Bar', 2: 'Baz', Foo: 0, Bar: 1, Baz: 2}
どういうこと?プロパティアクセスの[]
の内部で式を定義して、そこでプロパティの値を入れているなら別々にやればいいのでは?と思ったけどそういうわけにもいかないのかな。難しい。
Unionに置き換える
enumをUnion Typesに置き換えることでenumを使わないようにできる。
const color = ['red', 'green', 'blue'] as const
type Color = typeof color[number] // => 'red' | 'green' | 'blue'
数字ではなく名前を指定する場合も簡単にできる。
const color = {
red: 'Red',
green: 'Green',
blue: 'Blue'
} as const
type Color = typeof color[keyof typeof color] // => 'Red' | 'Green' | 'Blue'
const red: Color = color.red // => 'Red'
enumの不透明性はUnion Typesでは実現できない。
文字列のenumを使うとき、ある文字列や文字列リテラルがenumに含まれることが文脈的に明らかであったとしても、それらをenumに割り当てることはできません。
enum StringEnum {
Foo = 'foo'
}
const foo1: StringEnum = StringEnum.Foo; // no error
const foo2: StringEnum = 'foo'; // error!!
(上記コードは記事内から引用)
この例はとても分かりやすい。
enumのデメリット
結構色々あるんだなと思ったけど、直感的で分かりやすいのは
- constでないenumはTypeScriptの”a typed superset of JavaScript”というコンセプトにそぐわない
- 数値のenumは型安全ではない
- 文字列のenumの宣言は冗長になることがある
だった。
ESLintで縛る
typescript-eslintのno-restricted-syntax
というルールでenumを使うと怒ってくれるらしい。
{
"rules": {
"no-restricted-syntax": [
"error",
{
"selector": "TSEnumDeclaration",
"message": "Don't declare enums"
}
]
}
}
これ使います。