飽きっぽい人のブログ

プログラマとしてもテスターとしても中途半端な人のブログ

TypeScriptで疑似的な文字列enumを作る

※2015/09/24追記 やはりいろいろ問題があるようなのでこの方法は使わないほうが良いです。

知っての通り、TypeScriptのenumには文字列を設定することはできない。
下記のようにすればenumのメンバ名の文字列を取得することはできる。(TypeScript1.6を使用)

export enum Fruits {
    BANANA=1,
    APPLE=4,
    ORANGE=5
}


export function showFluits(fruits:Fruits){
    //これで文字列をとりだせる
    let fruitsStr = Fruits[fruits];
    console.log(fruitsStr);
}


showFluits(Fruits.APPLE);//APPLEと表示される

しかし文字列を定数として扱うことのみを考えた場合、TypeScriptのenumではやや冗長すぎるJavaScriptが生成される。

生成されたJavaScript

export var Fruits;
(function (Fruits) {
    Fruits[Fruits["BANANA"] = 1] = "BANANA";
    Fruits[Fruits["APPLE"] = 4] = "APPLE";
    Fruits[Fruits["ORANGE"] = 5] = "ORANGE";
})(Fruits || (Fruits = {}));
export function showFluits(fruits) {
    //これで文字列をとりだせる
    let fruitsStr = Fruits[fruits];
    console.log(fruitsStr);
}
showFluits(Fruits.APPLE); //APPLEと表示される


const変数やオブジェクトリテラルを使用してもよいのだが、それだとせっかくのTypeScriptの型の恩恵を受けることができない。
showFluits関数の引数fruitsがstring型になった場合を考えてみてほしい。その関数を使うユーザは何の文字列を引数にしてよいか戸惑ってしまうだろう。
それで型の恩恵を受けつつ文字列定数を定義する方法はないか考えた結果以下のようになった。

export interface Fruits extends String{}

//インターフェースと同じ名前の名前空間
export namespace Fruits{
	export const BANANA:Fruits="BANANA";
	export const APPLE:Fruits = "APPLE";
	export const ORANGE:Fruits = "ORANGE";
}

export function showFruits(fruits:Fruits){
	console.log(fruits);
}

showFruits(Fruits.APPLE);//APPLEと表示される

生成されたJavaScript

export var Fruits;
(function (Fruits) {
    Fruits.BANANA = "BANANA";
    Fruits.APPLE = "APPLE";
    Fruits.ORANGE = "ORANGE";
})(Fruits || (Fruits = {}));
export function showFruits(fruits) {
    console.log(fruits);
}
showFruits(Fruits.APPLE); //APPLEと表示される

まあ若干裏技感があるんだけどね。