TypeScriptで任意の要素数を持つ配列型を定義する

固定長の配列の型は、たとえば以下のように定義できます。

type VectorArray3 = [number, number, number]

任意の長さの固定長の配列の型を以下のように定義できると便利だなと思いました。

type VectorArray3 = ArrayFixedLength<3, number>

ArrayFixedLength を Conditional Types を使って以下のように書いてみました。

type ArrayFixedLength<T extends number, U, V extends Array<U> = []> =
    V[T] extends U ? V : ArrayFixedLength<T, U, [...V, U]>

以下のように評価されることで、最終的に T に設定した整数値分だけの要素を持つ配列を作成します。

  • まずは V[T] extends U が評価される。最初はVの型は空の配列型([])なので、仮に T が 0型だとすると T[0] は undefined となる。このため3番目のオペラントである ArrayFixedLength<T, U, [...V, U]> が評価される。ArrayFixedLengthのジェネリクス型のうち3番目の型を [...V, U](つまり [...[], U] なので [U])として AraryFixedLength 型を返却する
  • 再帰的に ArrayFixedLength を評価するので、再び V[T] extends U が評価される。今度はVの型は [U] 型になっている。Tが0なら V[T] (つまりV[0])の型は U型になるので、今度は2番目のオペラント(V)が評価され、最終的に [U] 型が返却される
  • 同様に、T = 1 なら [U, U] が、T = 2 なら [U, U, U] 型が返却されることになる

この方法だと、3つの要素を持つ配列を定義するには、以下のように ArrayFixedLength に渡す 1番目の型を 2 にしなければならず、やや直観的ではありません。

type VectorArray3 = ArrayFixedLength<2, number>

上記の ArrayFixedLength をさらに書き換えて以下のようにしてみます。

type ArrayFixedLength<T extends number, U, V extends Array<U> = [], W extends Array<true> = [true]> = 
    W[T] extends true ? V : ArrayFixedLength<T, U, [...V, U], [...W, true]>

再帰評価の終了条件を判定するためのW型を定義することで、以前の ArrayFixedLength よりも一つ少ない要素数の型が返却されます。

type VectorArray3 = ArrayFixedLength<3, number>

これで、上記の評価結果は [number, number, number] 型になり、やりたかったことが実現できました。 TypeScript Playground でも結果を確認できます。

(この方法でやりたかったことは実現できたのですが、「ある配列型の要素を一つ減らした配列型を再定義する」方法のもっと良いやり方があったらぜひ知りたいです...)

型の操作による型の生成は、少ない命令でやりたいことを実現する必要がありパズルのようで、ついつい時間を使ってしまいます。

ではでは。

このカウンタは @piyoppi/counter-tools を使っています。

クリックすると匿名でいいねできます。