https://zenn.dev/dqn/articles/union-to-tuple

og-base_z4sxah.png

次のように、union の構成要素を 1 つずつ持つ tuple の型を定義したいときの話です。

type T1 = UnionToTuple<"a" | "b" | "c">;
// => ["a", "b", "c"];

TypeScript で union to tuple をする方法として 2 通りの方法がよく知られていますが、この記事ではそれらの方法が不安定である理由を解説しています。

なぜ難しいのか

TypeScript のリポジトリに union to tuple の機能を提案する issue があり、そこに理由がよくまとめられたコメントが書かれています。

このコメントでは 2 通りの方法について言及しています。

1. 関数のオーバーロードを利用する方法

複数の Function Type Expressions からオーバーロードされた関数の型を作るときは次のようにしてできます。

type T1 = () => string;
type T2 = () => number;

type T3 = T1 & T2;

このとき、Type Inferernce を使って T3 の返り値の型を取り出すと、最後にオーバーロードした関数の返り値の型である number を返します[1]

type T4 = T3 extends () => infer R ? R : never;
// => number

この仕様を利用すると、次のようなステップを踏めば union を tuple に変換できそうです。

  1. A | B | C() => A | () => B | () => C に変換する
  2. union から intersection (() => A & () => B & () => C) に変換する
  3. 先述の方法で型を 1 つだけ取り出し、tuple に追加する
  4. 構成要素がなくなるまで「3」を繰り返す

コードは次のようになります。