本稿ではTypeScriptで、共用体型(union type)を交差型(intersection type)に変形するmapped typesを紹介します。
共用体型を交差型に変換するmapped types
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
出典はTypescript Convert Union to intersection - Stack Overflowです。一応上のスニペットは、Stack Overflowの回答からの原文ママの引用なので、ライセンスは回答にあるソースコードを別の場所で使っても構いませんか? - スタック・オーバーフローMetaを参照してください。
使い方
まず交差型にしたい共用体型を準備します。ここではEither(どっちかという意味)という型を用意しました。{a: number}か{b: number}のどちらかを満たせばOKなやつです。
type Either = {a: number} | {b: number}
念の為、共用体型の性質を確認しておきます。
const a: Either = {} // Compile error: Property 'a' is missing
const b: Either = {a: 1}
const c: Either = {b: 1}
const d: Either = {a:1, b: 1}
上のコードではaだけが型を満たせないのでコンパイルエラーになります。
次に、先程のUnionToIntersectionを使って上のEitherから、{a: number} & {b: number}を錬成したいと思います。ここではBoth(どちらもの意)という型名にします。
type Both = UnionToIntersection<Either>
ちゃんと交差型になっているか確認します。
const p: Both = {} // Compile error: Property 'a' is missing
const q: Both = {a: 1} // Compile error: Property 'b' is missing
const r: Both = {b: 1} // Compile error: Property 'a' is missing
const s: Both = {a:1, b: 1}
上のコードのs以外がコンパイルエラーになりました。ちゃんと交差型になっているようです。
この検証をやってみたい方はTypeScript playgroundで試せます。
解説
いろいろ試行錯誤はしてみましたが、ついになぜ動くのか理解できませんでした。説明できる方、いましたら教えてください ![]()
実用例
このテクニックの実用例を @kahirokunn に教えてもらいました。VuexにmapComputedのようなヘルパー関数を定義するときに応用すると、型安全なコードが書けるようです。