2017-10-09 15 views
2

オブジェクト際に、このような労働組合を超えるマッピング:徹底的なマップは、私は活字体の網羅を強制したい

type Union = 
    { type: 'A', a: string } | 
    { type: 'B', b: number } 

連合イベントハンドラ:

const handle = (u: Union): string => 
    theMap[u.type](u); 

は、それがあれば素晴らしいことだろうここで何らかの形でTypeScriptから完全性検査を受けることができます:

const theMap: { [a: string]: (u: Union) => string } = { 
    A: ({a}: { type: 'A', a: string }) => 'this is a: ' + a, 
    B: ({b}: { type: 'B', b: number }) => 'this is b: ' + b 
}; 

答えて

4

与えられたタイプUnionで定義されているように、TypeScriptをコアワイズすることは難しいことです(theMapは、ユニオンの各構成要素タイプに対して厳密に1つのハンドラを含みます)。健全性の制約(の各ハンドラはfor a 特定ユニオンの構成タイプ)。

しかし、上記の制約を表すことができる、より一般的なタイプの意味でUnionを定義することは可能です。最初に、より一般的なタイプを見てみましょう:ここ

type BaseTypes = { 
    A: { a: string }; 
    B: { b: number }; 
} 

BaseTypesは彼らから削除typeと構成のタイプに元Uniontype財産からのマッピングです。これより、Union({type: 'A'} & BaseTypes['A']) | ({type: 'B'} & BaseTypes['B'])に相当します。

さんはBaseTypesのようなタイプのマップ上のいくつかの操作を定義してみましょう:

type DiscriminatedType<M, K extends keyof M> = { type: K } & M[K]; 
type DiscriminatedTypes<M> = {[K in keyof M]: DiscriminatedType<M, K>}; 
type DiscriminatedUnion<M, V=DiscriminatedTypes<M>> = V[keyof V]; 

あなたはUnionDiscriminatedUnion<BaseTypes>と同等であることを確認することができます

type Union = DiscriminatedUnion<BaseTypes> 

さらに、それはNarrowedFromUnionを定義すると便利です:

type NarrowedFromUnion<K extends Union['type']> = DiscriminatedType<BaseTypes, K> 

wh ichはキーKを受け取り、typeでそのユニオンの構成要素を生成します。だからNarrowedFromUnion<'A'>は組合の片足であり、NarrowedFromUnion<'B'>はもう一方であり、一緒になってUnionを構成する。

は、今、私たちはtheMapのタイプを定義することができます。

const theMap: {[K in Union['type']]: (u: NarrowedFromUnion<K>) => string } = { 
    A: ({ a }) => 'this is a: ' + a, 
    B: ({ b }) => 'this is b: ' + b 
}; 

それは、その特定のタイプstringからから機能がある、Unionに各タイプごとに1つのプロパティを含むmapped typeです。これは網羅的なものです。AまたはBのいずれかを省略するか、またはプロパティにB関数を入れると、コンパイラは不平を言います。

つまり、タイプtheMapがこの制約を適用しているため、{a}{b}の明示的な注釈を省略することができます。あなたのコードからの明示的な注釈は本当に安全ではなかったので、これは良いことです。コンパイラによって警告されていない注釈を切り替えることができました。なぜなら、入力がUnionであることが分かっていたからです。(関数のパラメータの不健全なタイプ狭窄のこの種は、bivarianceと呼ばれ、それが活字体中の混合祝福ですされています。)

今、私たちはhandleが渡されたUnionパラメータのtypeに汎用的にする必要がありますので、コンパイラは、我々をチェックしますそれで安全な何かをやっている:

const handle = <K extends Union['type']>(u: NarrowedFromUnion<K>): string => 
    theMap[u.type](u); 

古い署名((u: Union) => string)を残した場合は、タイプアサーションでそれを黙らない限り、それは文句を言うでしょう。


これは大変でした。それが役に立てば幸い。がんばろう!

+1

...... ::::: badass ::::: ...... – Birowsky

関連する問題