[[タプル型 (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];
// .... 以下必要なだけ続く
```