TypeScriptのEnum
TypeScriptはJavascriptへのトランスパイル(変換)を前提として、型システムなど機能を増強した比較的新しい言語です。
で
そのうちの一つ、enum型(プリミティブ)がとてつもなく中途半端なことが原因で長い時間ハマったのでナレッジを記載します。
enumが中途半端とは
そもそもJavascriptにはenum型がありません。でもTypeScriptにはEnumがあります。
それを実現しているのはトランスパイラ。
つまり、「本当はない概念を、それらしく変換することでそう振る舞わせている」だけ
なので、言語システムとしてガッツリ組み込んでいるJavaやC#のそれだと思ってかかると悩みます。
enumだけど、他の言語のそれとは違うということを念頭に入れておきましょう
今回やりたかったこと
- HTTPリクエストヘッダーに含まれる特定の要素以外を全てコピーしたかった
- 除外ルールを作るため、以下の様に実装していたが、レビューで弾かれenum発令が出る
const expire: string[] = ['content-type']
Object.keys(req.headers).forEach( key => {
if (!expire.includes()) {
~コピーの処理~
}
})
結論
こう書けばできる
enum test {
AAA = 'content-type',
BBB = 'CCC'
}
console.log( (Object.values(test) as string[]).includes('content-type') )
出力結果
true
こうでないといけない理由
ひたすらtypeofとかで調べて以下の原因ことが挙げられます。
-
文字列をvalueに物enumはvalue値からの逆変換が言語仕様的にできない。(用意されてないという意味で)
- トランスパイル後のJSがkey, value共にstringになるので探しきれないらしい
-
enumはenum型でも、(上記でいう)test型でもなくobject型になるの
- だからObject.values(test).includes(...)で型が合わずにビルドが通らない
- でも結論コードのトランスパイル結果みるとその書き方に変換されているので、非常にシャク
-
〜in〜はkeyを比較してしまうので、Keyに指定できない文字列を探そうとするとinは使えない
上記の理由からどうしても生のTypeScriptでは、Object.valuesを使用することが避けられず、かつその戻り値か検索値をキャストする必要が生まれます。
後者をキャストすると
Object.values(test).includes('content-type' as test)
となってループの中だったり、enumを外部定義してるとソースの可読性が落ちたりという心配事が生まれます。(機械を信用しないタイプ)
なので、私の場合は結論コードになりました
余談
TypeScript、型システムはいいんだけどやっぱりJavascriptと機能に互換性がないものを使おうとするとめんどくさいです。
JVMで動くKotlinとかGroovyとかのトランスパイル言語もこんなめんどくささ抱えてるんでしょうか。。。
ちなみに、解決の決めては以下の質問からインスパイアされました