LoginSignup
0
0

More than 1 year has passed since last update.

TypeScriptでのenumの定義を整理する

Posted at

TypeScriptでenumを使いたいんじゃ

先日「TypeScriptでenumを定義したい!」ということがあり、その時に調べたことを整理しながらアウトプットしたいと思います。

とりあえず、enumは下記のように定義することができます。

enum Position {
  Top,
  Right,
  Bottom,
  Left,
}

ですが、調べてみるとどうやらこの記述には問題点があるそうです。
その問題点と解決方法を、こちらをもとに整理したいと思います。(いつも参考にさせてもらってます。ありがとうございます。ビッグラヴ)

問題点

1. 数値列挙型には型安全上の問題がある

数値列挙型とは、下記のように数値をenumで定義することです。

enum ZeroOrOne {
  Zero = 0,
  One = 1,
}

この数値列挙型、実はnumber型ならなんでも代入できてしまうという問題があるのです。
なので、上記のように0か1のみのenumを定義したつもりでも、それ以外の数値が代入できちゃいます。困ったもんだ。

そして、enumは下記のようにオブジェクト型でアクセスすると値が取得できます。

console.log(ZeroOrOne[0])
// => 'Zero'

これの何が問題なのかというと、存在しない値でアクセスしてもエラーが起きないことです。

console.log(ZeroOrOne[9])
// => undefined

…😫😫

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

型システムには公称型構造的部分型の二種類があり、TypeScriptは後者の構造的部分型を採用しています。
この2つの違いはこちらの記事を参考にさせていただきました。(これもsuinさんの記事)

公称型:2つのオブジェクトが全く同じプロパティを持っていても、継承関係が無ければ代入できない。(Java, C++など)
構造的部分型:2つのオブジェクトが全く同じプロパティを持っている場合、そのオブジェクトに代入できる。(Go, TypeScriptなど)

先程も書いたようにTypeScriptは構造的部分型を採用しているのですが、文字列列挙型(文字列を定義したenum)は例外的に公称型となるそうです。
ちなみに数値列挙型は公称型にはなりません。なぜなんだろうか…🤔
このようにばらつきが出てしまうのも問題とされています。

3. 呼びされない不要なコードがコンパイルされる

TypeScriptで定義されたenumは、即時関数としてコンパイルされます。
即時関数でコンパイルされると、Tree-shakingというwebpackなどでバンドルする際に実行されないコードを削除してくれる機能がうまく動作しないという問題があるそうです。

解決方法

1. union型を使う

union型とは、「いずれかの型」を表すものです。
この場合、Hoge型にはstring型かnumber型かundefined型が入ります。

type Hoge = string | number | undefined

enumとして使用する場合は下記のようになります。

type FrameWork = 'React' | 'Vue' | 'Angular'

type Props = {
  name: FrameWork
}

const Frontend = ({name}: Props) => {
  return (
    <p>{name}</p>
  )
}

// 呼び出し
<Frontend name={'React'} />

こうすることで、nameにはFrameWork型で定義されている3つの値しか指定できなくなります。

2. オブジェクトリテラルを使う

オブジェクトで型の値を定義します。
そのオブジェクトのプロパティをas constを使ってReadOnlyにし、さらにkeyof typeofを使って型として定義します。
keyof typeofについてはこちらの記事が分かりやすかったです🙌

const FrameWork = {
  React: 'React',
  Vue: 'Vue',
  Angular: 'Angular'
} as const

type FrameWork = typeof FrameWork[keyof typeof FrameWork]

type Props = {
  name: FrameWork
}

const Frontend = ({name}: Props) => {
  return (
    <p>{name}</p>
  )
}

// 呼び出し
<Frontend name={FrameWork.React} />

まとめ

enumは特に理由が無い場合使わない方が良さそうです。
他にもこんな方法があるよ〜などありましたら教えて下さい😌

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0