与えられたタイプUnion
で定義されているように、TypeScriptをコアワイズすることは難しいことです(theMap
は、ユニオンの各構成要素タイプに対して厳密に1つのハンドラを含みます)。健全性の制約(の各ハンドラはfor a 特定ユニオンの構成タイプ)。
しかし、上記の制約を表すことができる、より一般的なタイプの意味でUnion
を定義することは可能です。最初に、より一般的なタイプを見てみましょう:ここ
type BaseTypes = {
A: { a: string };
B: { b: number };
}
、BaseTypes
は彼らから削除type
と構成のタイプに元Union
のtype
財産からのマッピングです。これより、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];
あなたはUnion
がDiscriminatedUnion<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
)を残した場合は、タイプアサーションでそれを黙らない限り、それは文句を言うでしょう。
これは大変でした。それが役に立てば幸い。がんばろう!
...... ::::: badass ::::: ...... – Birowsky