Closed
Description
Update: converted to proposal.
Background
Currently, tuples are arrays that are restricted in minimum length, but not in maximum:
var t1: [number, number] = [1]; // this doesn't work
var t2: [number] = [1, 2]; // this works
This makes harder to predict types or errors in some scenarios:
var t1: [number, string];
var t2 = [...t1, ...t1]; // could be inferred to be [number, string, number, string], but must be inferred as [number, string] (now it's simply inferred as (number|string)[])
var t3: [number, string] = [1, "a"];
t3[2]; // ok, but must be an error
There also might be difficult to adopt variadic kinds, especially in type construction:
function f<...T>(...rest: [...T]): [...T, ...T] {
var rest1: [...T] = [...rest, 1]; // it will be acceptable due to current rules
return [...rest1, ...rest1]; // due to types it seems to be [...T, ...T], but actually is [...T, number, ...T, number]
}
Proposal
(1) Restrict tuple instance to match exact length
var t1: [number, string] = [1, "a"]; // ok
var t2: [number, string] = [1]; // error (existing)
var t3: [number, string] = [1, "a", "b"]; // error (new)
(2) Introduce open length tuple types
Open-length tuple types will be the same as tuple types are now.
var t1: [number, string, ...] = [1, "a", 2, "b"]; // same as current tuples are now
var t2: [number, string, ...(number|string|boolean)[]]; // explicitly type rest elements -- syntax option 1 -- consistent with rest parameters
var t3: [number, string, ...number|string]; // explicitly type rest elements -- syntax option 2
// strict tuple type can be implicitly converted to open length tuple type
var t4: [number, string, string];
var t5: [number, string, ...] = t4; // ok
var t6: [number, ...] = t4; // error, 'number|string' cannot be converted to 'number'
var t6: [number|string, ...] = t4; // ok
var t7: [number, ...(number|string)[]] = t4; // ok
(3) Improve contextual type inference for spread operators on tuples
var t1: [number, string];
var t2: [number, string, number, string] = [...t1, ...t1]; // it's proven now
var t3: [number, ...];
var t4: [string, ...];
var t4: [number, number|string, ...] = [...t3, ...t4]; // this also can be proven
Related issues
This addresses:
- out of range index in a tuple magically works #5203 - out of range index in a tuple magically works /cc @Aleksey-Bykov @RyanCavanaugh @DanielRosenwasser
- Proposal: Variadic Kinds -- Give specific types to variadic functions #5453 - variadic kinds /cc @sandersn @JsonFreeman
Disadvantages
This is definitely a breaking change.