Boilerplateのない同じタイプスクリプトenum間のマッピング


型紙のenumsは変です.彼らは、構造/ダックタイピングパターンに従わないTS(私が気づいている限り)の唯一の部分です.文字列の代わりにenumを好むなら、以下のようにしてください.

あなたは同じメンバーを含む2つのenumを持っています、しかし、TSは互換性があると理解しません.今、私は3つの主要なパターンが問題を解決するために利用するのを見ます:

  • IDCのapproach:あなたはどこでキャスト.これは最も簡単ですが、それは危険です、enumのキャストとしては、ほとんど何かを行うことができます.

  • purist :型は異なっていますので、類似点を無視してマッパー関数(またはオブジェクト)を作成し、必要に応じて使用します.
    これは確かに非常に安全ですが、メンテナンスオーバーヘッドの多くを追加し、noooootのためのコード(バンドル)サイズがたくさんの理由がたくさん.

  • 中間の地面:あなたは機能をつくりますが、中で投げられます.
    一度だけキャストしたように、これは紙の上にアプローチ1よりもよく見えます、しかし、あなたは本当に同じ危険にさらされています:2つのenumsがどんな点ででも分岐するならば、あなたはTSから一つの警告を得ません.
  • ボイラ板なしの安全性


    私は、私たちがなんとか1/2のタイプ安全性を得ることができると思いました、そして、また、どれくらいの純度と束サイズを保持しますか?それはわかる.
    JSレベルでは、この3番目のアプローチは些細なことですが、マッパー機能は本質的ですx => x .
    だから今では“ちょうど”私たちは同一のenums間のマップを許可されていることを確認するためにTSの制約を追加する問題です.

    実装


    コア


    私の現在の解決は、それが非常によく拡張されることができたけれども、単純(同じキーと値)enumsで単純に働きます.では、最初に対称なenumを定義しましょう.
    type SymmetricalEnum<TEnum> = {
      [key in keyof TEnum]: key;
    };
    
    そして、ここでは、トリックです-マッピングの結果値が何であるかを定義することができます、これは本質的にその利点のすべてで、TSメタ言語レベルのマッピングをしています:
    type MapperResult<
      TSourceEnumObj,
      TDestEnumObj extends SymmetricalEnum<TSourceEnumObj>,
      TSourceValue extends keyof TSourceEnumObj
    > = TDestEnumObj extends { [key in TSourceValue]: infer TResult } ? TResult : never;
    
    私たちには、3つの一般的なargsがあります:
  • ソースenum型.
  • 宛先enum型.これは、タイプセーフティロジックのコアです.ソースのスーパーセットではないenumにマップしようとすると、この行はここで叫びます.
  • マッパー関数に与えられた実際の入力値の型.それから、これは基本的に目的地タイプから対応する値を「引く」条件付声明で使われます.あなたが条件タイプで読む必要があるならばthe documentation .
  • タイプレベルのマッピングが必要な場合は、上記の魔法を使用してください.

    互換性のないenumは拒否されます.

    マッパ関数の作成


    このマジックを利用して、自動的にマッパー関数を生成する高次関数を作成する準備が整いました.
    const createEnumMapperFunction =
      <TSourceEnumObj, TDestEnumObj extends SymmetricalEnum<TSourceEnumObj>>(from: TSourceEnumObj, to: TDestEnumObj) =>
      <TInput extends keyof TSourceEnumObj>(value: TInput) =>
        value as MapperResult<TSourceEnumObj, TDestEnumObj, TInput>;
    
    ここでは1つのエラーが含まれています.

    そしてこれです!今、あなたは電話ですべてのスイッチ文を置き換えることができますcreateEnumMapperFunction , または、上記のようなタイプセーフバージョンでキャストを置き換えます.非対称enumsまたは他のケースのサポートを追加するようなあなたのニーズに微調整すること自由に感じなさい.

    最終コード


    スタックオーバーフローキーボードを持っている場合
    ここでは完全なコード、およびTS Playground with examples :
    type SymmetricalEnum<TEnum> = {
      [key in keyof TEnum]: key;
    };
    
    type MapperResult<
      TSourceEnumObj,
      TDestEnumObj extends SymmetricalEnum<TSourceEnumObj>,
      TSourceValue extends keyof TSourceEnumObj
    > = TDestEnumObj extends { [key in TSourceValue]: infer TResult } ? TResult : never;
    
    const createEnumMapperFunction =
      <TSourceEnumObj, TDestEnumObj extends SymmetricalEnum<TSourceEnumObj>>(from: TSourceEnumObj, to: TDestEnumObj) =>
      <TInput extends keyof TSourceEnumObj>(value: TInput) =>
        value as MapperResult<TSourceEnumObj, TDestEnumObj, TInput>;
    
    私はあなたがこの便利な日を見つける願っています.あなたがしたならば、コメントで私に知らせてください、そして、あなたが改善されたバージョンを造ったならば、私も知っていたいです.