[[タプル型 (TypeScript)|タプル型]]の機能が大幅に強化され、関数表現の幅が広がった[[TypeScript]]の型表現。最大のポイントは[[型パラメーター (TypeScript)|型パラメーター]]を使った引数可変の[[タプル型 (TypeScript)|タプル型]]を定義できるようになったこと。 `<TS extends unknown[]>`に対して、`[number, ...TS]`のような型定義できる。以前は`[number, ...T[]]`のように[[型パラメーター (TypeScript)|型パラメーター]]とは別に配列であることが`[]`で明示されていなければ使えなかった。 これは **可変長の引数をもつ関数を的確に推論させたいとき** メリットがある。 ## tail関数の例 `tail`は1つ以上の可変長引数をとり、先頭を除去した配列か[[タプル (JavaScript)|タプル]]を返却する関数。 ```ts tail(1, "a", true, 2, "b"); // -> ["a", true, 2, "b"] ``` [[可変長タプル型]]の機能を使って`tail`関数を実装してみる。 ```ts function tail<TS extends unknown[]>(...args: [any, ...TS]): TS { const [_, ...rest] = args; return rest; } ``` `<TS extends unknown[]>`は **TSは何かしらの配列/Tuple型** という意味。 `(...args: [any, ...TS]): TS`は少しややこしいので、`TS`が`T`型の要素3つから構成された配列と仮定して順に展開してみる。 1. `(...args: [any, ...TS]): TS` 2. `(...args: [any, ...[T, T, T]]): [T, T, T]` 3. `(...args: [any, T, T, T]): [T, T, T]` 4. `(args[0]: any, args[1]: T, args[2]: T, args[3]: T): [T, T, T]` つまり、以下の様に解釈できる。 - 第1引数が`any` - 第2引数以降が0個以上の`T`型の引数 - 戻り値が第2引数以降と同じ数からなる`T`型の値 `tail`関数を使った結果は的確に推論される。 ```ts const strs = tail(1, "a", "b"); // -> [string, string]と推論される const nums = tail("a", 1, 1); // -> [number, number]と推論される const mix = tail(1, "a", true, 2, "b"); // -> [string, boolean, number, string]と推論される const first = tail("hoge") // -> []と推論されるnumber, string]と推論される ``` ## メリット 今までは気合いのオーバーロードでしかできなかった関数の型表現をシンプルに実現できること。[[TypeScript 4.0]]以前だと恐らく以下のような定義が必要になる。 ```ts function tail(head: any): []; function tail<A>(head: any, arg1: A): [A]; function tail<A, B>(head: any, arg1: A, arg2: B): [A, B]; function tail<A, B, C>(head: any, arg1: A, arg2: B, args3: C): [A, B, C]; // .... 以下必要なだけ続く ```