ts.svg

【TypeScript】列挙型

TypeScript

列挙型とは

列挙型は、Java や C#などと同じく名前付きの定数を定義するためのものです。 定義にはenumを使用し、定数名はすべて大文字とします。

enum Status {
  SUCCESS,
  WARN,
  ERROR,
}
switch (s) {
  case Status.SUCCESS:
    console.log('SUCCESS')
    break
  case Status.WARN:
    console.log('WARNING')
    break
  case Status.ERROR:
    console.log('ERROR')
    break
}

TypeScript の列挙型は、以下より説明する数値と文字列によって定義できます。

数値列挙型

定数の値に数値を設定します。

enum Status {
  SUCCESS = 1,
  WARN = 2,
  ERROR = 3,
}
const s = 1
console.log(s === Status.SUCCESS) //true
console.log(s === Status.WARN) //false

数値は省略することができ、この場合 0 からの連番が設定されます。

enum Status {
  SUCCESS, //0
  WARN, //1
  ERROR, //2
}

途中で数値を設定した場合は、その数値からの連番が設定されます。

enum Status {
  SUCCESS, //0
  WARN = 3, //3
  ERROR, //4
}

文字列列挙型

定数の値として文字列を設定します。 数値型と違い、省略することはできません。

enum Status {
  SUCCESS = 'success',
  WARN = 'warning`,
  ERROR = 'error'
}
const s = 'success'
console.log(s === Status.SUCCESS) //true
console.log(s === Status.WARN) //false

open ended

同じ名前の列挙型を定義した場合、その結果はマージされます。

enum Status {
  SUCCESS,
}
enum Status {
  WARN = 1,
  ERROR,
}

2 つ目以降に定義する最初の値は、省略することができません。

定数列挙型

次のような列挙型があるとします。

enum Status {
  SUCCESS,
  WARN,
  ERROR,
}
const s = Status.SUCCESS

これを JavaScrpt にコンパイルすると、以下のようになります。

var Status
;(function (Status) {
  Status[(Status['SUCCESS'] = 0)] = 'SUCCESS'
  Status[(Status['WARN'] = 1)] = 'True'
  Status[(Status['ERROR'] = 2)] = 'ERROR'
})(Status || (Status = {}))
const s = Status.SUCCESS

このように列挙型に該当するオブジェクトが作成されます。 値を使用する際は、このオブジェクトを参照する必要があります。

では次のようにconstを付与します。

const enum Status {
  SUCCESS,
  WARN,
  ERROR,
}
const s = Status.SUCCESS

これを JavaScript にコンパイルすると、以下のようになります。

const s = 0

列挙型に該当するオブジェクトは作成されず、その値のみが必要な箇所で使われます。 見てわかる通り、constを付与することでパフォーマンスの向上が見込めます。

--preserveConstEnumsを設定することでconstを付与しても、列挙型に該当するオブジェクトが作成されるようになります。

const assertion

実は列挙型には問題があり、使用しない方が良いという説があります。

参照:https://www.kabuku.co.jp/developers/good-bye-typescript-enum

参照:https://engineering.linecorp.com/ja/blog/typescript-enum-tree-shaking/

以下に簡単な問題点をあげます。

const enum Status {
  SUCCESS,
  WARN,
  ERROR,
}
function func(s: Status) {
  console.log(s)
}

func(Status.SUCCESS) //0
func(10) //10

funcの引数にはStatusを設定していますが、実は数値であればエラーなく実行することができてしまいます。

ここで、列挙型の代わりに使用するのが const assertion です。 const assertion は、TypeScript3.4 から導入された機能で、推測される型の不変性を担保するためのものです。 よくわからないと思うので、以下の例を見てください。

let x = [0, 1]
type XT = typeof x //number[]

let y = [0, 1] as const
type YT = typeof y //readonly [0, 1]

xnumber[]の型を持ちますが、as constを付与したyreadonlyのタプル型となります。 つまり、xの値は変更可能ですが、yは変更不可となります。

これをオブジェクトで使用してみます。 オブジェクトにas constを付与すると、各プロパティにreadonlyが付与された型となります。

const obj = {
  x: 0,
  y: 1,
  z: 2,
} as const
type T = typeof obj
//{
//  readonly x: number,
//  readonly y: number,
//  readonly z: number
//}

では、const assertion を使用して列挙型を置き換えてみます。

const Status = {
  SUCCESS: 0,
  WARN: 1,
  ERROR: 2,
} as const
type Status = typeof Status[keyof typeof Status] // 0 | 1 | 2
function func(s: Status) {
  console.log(s)
}

func(Status.SUCCESS) //0
func(10) //Error

列挙型より定義が少しだけ手間かもしれませんが、先程あげた問題を解決することができます。