generic関数宣言内のVariadic Tuple Types
前提条件
genericな関数に型をつけるときに使える
型引数には、arrayかtupleの制約が入っている必要がある
XS extends unknown[]
XS extends readonly unknown[]
例.tsdeclare function hoge<XS extends unknown[]>(
values: readonly [...XS]
): ...
どう変わるのか
上の前提条件を満たしているときに、 [...XS]
と書くと、
XS
が、配列型ではなく、Tuple型として推論される
XS
と
[...XS]
で意味が変わるの難しすぎない?

意味が変わっている例
普通にただの XS
と書いた時、
tsconst fn = <XS extends readonly unknown[]>(arr: XS): XS => arr
const hoge = fn(['foo', 1]); // (string|number)[]
返り値は、 ['foo',1]
だが、 (string|number)[]
と推論される
[...XS]
と書いた時
tsconst fn = <XS extends readonly unknown[]>(arr: [...XS]): XS => arr;
const t = fn(['foo', 1]); // [string,number]
返り値は、 [string,number]
と推論される
実際に使っている例
tupleに対するindex accessに対する推論
ちょっとだけ中心機能とズレている話だけど、これもすごいな

tsfunction head<T extends readonly any[]>(args: readonly [...T]) {
const result = args[0];
return result;
}
const str = head(["foo", "bar"] as const); // "foo"
tupleでないと、効能は薄れるが、
tsfunction head<T extends readonly any[]>(args: [...T]) {
const result = args[0];
return result;
}
const str = head(["foo", "bar"]); // string
const num = head([1, "bar"]); // number
tupleじゃないと、
[1, 'bar']
なんてリストは作らないので、結局tuple使う時に嬉しい、という話だな

参考