本稿では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のようなヘルパー関数を定義するときに応用すると、型安全なコードが書けるようです。