時計を壊せ

駆け出してからそこそこ経ったWebプログラマーの雑記

TypeScriptでタプル型の順列を得たい

たとえば、 [1, 2, 3] というタプル型があった場合に [1, 2, 3] | [1, 3, 2] | [2, 1, 3] | [2, 3, 1] | [3, 1, 2] | [3, 2, 1] みたいな組み合わせが欲しい。 これは順序を指定するようなケースに型制約を持たせるときに役立ち、io-tsなどを使ってType Guard関数を生成すれば外部入力に対しても型エラーが捕捉できる。

サッとググった感じ、自分のググりパワーが足りないのかうまいソリューションがみつからなかったので、あーでもないこーでもないとやって諦めてベタッと書いたところこんな感じになった。

type Permutations2<T extends readonly any[]> = [T[0], T[1]] | [T[1], T[0]];
type Permutations3<T extends readonly any[]> =
  | [T[0], ...Permutations2<[T[1], T[2]]>]
  | [T[1], ...Permutations2<[T[0], T[2]]>]
  | [T[2], ...Permutations2<[T[0], T[1]]>];
type Permutations4<T extends readonly any[]> =
  | [T[0], ...Permutations3<[T[1], T[2], T[3]]>]
  | [T[1], ...Permutations3<[T[0], T[2], T[3]]>]
  | [T[2], ...Permutations3<[T[0], T[1], T[3]]>]
  | [T[3], ...Permutations3<[T[0], T[1], T[2]]>];
type Permutations<T extends readonly any[]> = {
  2: Permutations2<T>;
  3: Permutations3<T>;
  4: Permutations4<T>;
}[T["length"] extends  2 | 3 | 4 ? T["length"] : never];

もっとうまくできそうな気がするが無限再起と判定されたり難しかった。n行ぶん記述すれば良いので雪だるま式に記述量が増えることもないが、添字を書き間違えたら変なタプル型が混じることになるのは微妙っぽい。 無限再起を回避する方法はboost-tsの実装とか参考にしたけど定数ぶんまでのパターンしかサポートしないということにするしかないのだろうか。

もっとうまく書けるぜ!ってひといたら教えて下さい。

これだった(教えてもらった):

susisu.hatenablog.com

やっぱ素直に再帰すると無限再帰扱いでエラーになるんだなぁ。 ベタに書くのも悪くないだろうか。

こういうのもあると教えてもらった:

github.com

シンプルでよさそうだけど無限再帰エラーにかからないのはobject型(っていうのかこれ?)をつかっているからだろうか?