Description
Bug Report
🔎 Search Terms
variadic tuple optional element rest spread inference
🕗 Version & Regression Information
- This is the behavior in every version I tried, and I reviewed the FAQ for entries about spreading tuples with optional elements
⏯ Playground Link
Playground link with relevant code
💻 Code
type T1 = [number?];
type T2 = [...T1, string]; // [number | undefined, string], but should be [number | undefined, string] | [string]
const t1: T1 = [];
const t2: T2 = [...t1, "hello"]; // ["hello"]
declare function getUser(id: string, options?: { x?: string }): string;
declare function getOrgUser(id: string, orgId?: number, options?: { x?: string }): void;
function callApi<T extends unknown[] = [], U = void>(method: (...args: [...T, { x?: string }]) => U) {
return (...args: [...T]) => method(...args, {});
}
callApi(getUser); // (id: string) => string
callApi(getOrgUser); // (id: string, orgId?: string | undefined) => void, but should be (id: string, orgId: string | undefined) => void
🙁 Actual behavior
As pointed out when middle and leading rest elements in tuple types were introduced, optional elements are spread as if the element are non-optional but possibly undefined (1) (2). When a tuple with omitted optional values is spread as a non-trailing rest into a tuple, the values are shifted and don't match the tuple's type.
Related inference behavior was addressed in #39607, but currently a variadic rest tuple with optional elements can be inferred when the rest tuple is followed by non-optional elements. This is incorrect, because the inferred type permits omitted values, resulting in invalid spreads where elements are shifted around.
🙂 Expected behavior
Spreading a tuple with optional elements should result in a type that accounts for potentially omitted optional elements.
Similarly, inferred variadic rest tuples should be restricted to only non-optional elements when followed by non-optional elements, because permitting omitted values results in invalid spreads where elements are shifted around.