TypeScript 任意の数の引数をとるが、一番最後だけ特定の型を受け取る関数の書き方

Variadic tuple typesを使用したサンプルです。TypeScript4.0以降にする必要があります。

例えば、任意のstringを受け取るが、最後の引数だけはnumberを受け取る関数を定義してみます。

declare function f1(...args: [...string[], number]): string

f1('aaa', 'bbb', 123) //OK
f1('aaa', 'bbb') // Type 'string' is not assignable to type 'number'
f1(123) //OK
f1() //Source has 0 element(s) but target requires 1.

これまでは、タプルの中でレストパラメータ(…)を使う場合は、[number, …string[]]のようにタプルの最後で指定する必要がありました。

Variadic tuple types の導入によって、最後以外でもレストパラメータが使えるようになりました。

以下のf2という関数は任意の個数の引数を受け取りますが、少なくとも2つの引数を受け取り、最初の引数はstringで最後の引数はnumberです。

そして、戻り値は最初と最後の引数以外の部分のタプルになります。

declare function f2<T extends unknown[]>(...args: [string, ...T, number]): T

f2('a', true, 123, 123) // 戻り値は [boolean, number]
f2('a', 'b', 123, 123) // 戻り値は [string, number]
f2('a', 'b', 123) // 戻り値は [string]
f2('a', 123) // 戻り値は []
f2(123, 'a') // Argument of type 'number' is not assignable to parameter of type 'string'.
f2('a') // Source has 0 element(s) but target requires 1.

https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html#variadic-tuple-types

補足

ちなみにタプル型のメリットは、配列と違って要素数と順序も型としてチェックしてくれるところです。

以下に配列の場合とタプルの場合でのTypeScriptが推論する型を示します。

// 配列
declare function f1<T extends unknown[]>(arr: T): T
// タプルを使う
declare function f2<T extends unknown[]>(arr: [...T]): T


f1(['aaa', 'bbb']) // 戻りは string[]
f2(['aaa', 'bbb']) // 戻りは [string, string] 要素数と順番も見られている

f1(['aaa', 123]) // 戻りは (string | number)[]
f2(['aaa', 123]) // 戻りは [string, number] 要素数と順番も見られている