JavaScript のスーパーセットであるところの TypeScript には幾つかの固有構文があり、その中のひとつに enum があります。
例えば以下のように enum MOBILE_OS
をおくと、以後のコードで MOBILE_OS.IOS
などとして定数のように振る舞えるものです。
enum MOBILE_OS { IOS, ANDROID } const os: MOBILE_OS = MOBILE_OS.IOS;
ところがこの enum、複数の理由から「使わない方が良い」とされています。 細かい理由については本稿の先行エントリを読んでいただくとして、大きく2つの理由が挙げられることが多いようです。
- Tree-shaking できない
- Babel でトランスパイルできない(const enum の場合)
本稿は、この2点に関して、先行エントリを踏まえて現行バージョン(TypeScript 4.2.3, Babel 7.13.12)で追試をすることで、より新しい情報を世に残しておこうとするものであります。 ついでに何かやった気になるやつになります。イェイイェイ
検証環境
- TypeScriptからJavaScriptへのトランスパイルは https://www.typescriptlang.org/play (TypeScript
3.9.24.2.3 / targetはESNext) で行いました。- Tree-shaking の挙動については https://rollupjs.org/repl/ にトランスパイルしたJavaScriptコードを貼り付けて検証しました。
- Babelを使用したトランスパイルを検証する際には https://babeljs.io/repl (Babel
7.10.37.13.12) で行いました。
TypeScriptのenumを使わないほうがいい理由を、Tree-shakingの観点で紹介します - LINE ENGINEERING より引用、バージョンを本稿環境に揃えた
Babel の設定はデフォルトより以下を弄りました。
- Source Type
- Script から Module にした
- PRESETS
- react をオフに、typescript をオンにした
実験1 Tree-shaking するのか
サンプルコード(先述のサイトより同様に引用)
export enum MOBILE_OS { IOS, ANDROID } // 文字列を割り当てた場合 export enum MOBILE_OS { IOS = 'iOS', ANDROID = 'Android' }
トランスパイルされたコード(https://www.typescriptlang.org/play より結果をコピー)
export var MOBILE_OS; (function (MOBILE_OS) { MOBILE_OS[MOBILE_OS["IOS"] = 0] = "IOS"; MOBILE_OS[MOBILE_OS["ANDROID"] = 1] = "ANDROID"; })(MOBILE_OS || (MOBILE_OS = {})); // 文字列を割り当てた場合 (function (MOBILE_OS) { MOBILE_OS["IOS"] = "iOS"; MOBILE_OS["ANDROID"] = "Android"; })(MOBILE_OS || (MOBILE_OS = {}));
結果1 Tree-shaking しない
https://rollupjs.org/repl/ の実行結果スクリーンショット。先行エントリ同様、使っていないコードが残っている
実験2 バベれるのか
const enum を対象にするので、サンプルコードに若干手を入れます。
export const enum MOBILE_OS { IOS, ANDROID } // 文字列を割り当てた場合 export const enum MOBILE_OS { IOS = 'iOS', ANDROID = 'Android' } export const isIOS = (os: MOBILE_OS) => os === MOBILE_OS.IOS
結果2 バベれない
https://babeljs.io/repl の実行結果スクリーンショット。未対応の旨エラー表示される
おわりに
今回参考にした先行エントリ2件はそれぞれ2020年の2月と7月のものであったので、半年も経てばなんか変わってないかな〜と思って追試の動機としましたが、残念なことに変化はなかった模様でした。 というか振り返ると、これは TypeScript というよりは Rollup と Babel の問題であるので、 TypeScript のメジャーバージョンが上がっていても関係ないと言われればごもっともかもしれない。
先行エントリにあるように、代替先としては Union Types があり、とはいえそれぞれに一長一短あるようなので、ユースケースに合わせて採用するのが良いのでしょう。なんでもケースバイケースって言えばいいと思いやがって!
個人的には困らない限り Union Types かな〜と思っています。 それはそれとして interface と type にもなんかこんな感じの話あったよなーとか思い出しました。また追試やるかもしれん。