Skip to content

Commit 090326b

Browse files
committed
Infer array rest as tuple if possible
Fixes: microsoft#26007
1 parent 0923771 commit 090326b

24 files changed

+124
-56
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4555,11 +4555,11 @@ namespace ts {
45554555
const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterables*/ false);
45564556
const index = pattern.elements.indexOf(declaration);
45574557
if (declaration.dotDotDotToken) {
4558-
// If the parent is a tuple type, the rest element has an array type with a union of the
4558+
// If the parent is a tuple type, the rest element has a tuple type of the
45594559
// remaining tuple element types. Otherwise, the rest element has an array type with same
45604560
// element type as the parent type.
45614561
type = isTupleType(parentType) ?
4562-
getArrayLiteralType((parentType.typeArguments || emptyArray).slice(index, getTypeReferenceArity(parentType))) :
4562+
sliceTupleType(parentType, index) :
45634563
createArrayType(elementType);
45644564
}
45654565
else {
@@ -8526,6 +8526,20 @@ namespace ts {
85268526
return links.resolvedType;
85278527
}
85288528

8529+
function sliceTupleType(type: TupleTypeReference, index: number) {
8530+
const tuple = type.target;
8531+
if (tuple.hasRestElement) {
8532+
// don't slice off rest element
8533+
index = Math.min(index, getTypeReferenceArity(type) - 1);
8534+
}
8535+
return createTupleType(
8536+
(type.typeArguments || emptyArray).slice(index),
8537+
Math.max(0, tuple.minLength - index),
8538+
tuple.hasRestElement,
8539+
tuple.associatedNames && tuple.associatedNames.slice(index),
8540+
);
8541+
}
8542+
85298543
function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type {
85308544
const type = getTypeFromTypeNode(node.type);
85318545
return strictNullChecks ? getOptionalType(type) : type;

tests/baselines/reference/declarationEmitDestructuring3.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ function bar([x, z, ...w]) { }
88
function foo([x, ...y] = [1, "string", true]) { }
99
>foo : ([x, ...y]?: [number, string, boolean]) => void
1010
>x : number
11-
>y : (string | boolean)[]
11+
>y : [string, boolean]
1212
>[1, "string", true] : [number, string, boolean]
1313
>1 : 1
1414
>"string" : "string"

tests/baselines/reference/declarationEmitDestructuringArrayPattern4.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ var _f = [1, "hello", true], x19 = _f[0], y19 = _f[1], z19 = _f[2], a13 = _f.sli
2222

2323
//// [declarationEmitDestructuringArrayPattern4.d.ts]
2424
declare var a5: number[];
25-
declare var x14: number, a6: number[];
26-
declare var x15: number, y15: number, a7: number[];
27-
declare var x16: number, y16: number, z16: number, a8: any[];
25+
declare var x14: number, a6: [number, number];
26+
declare var x15: number, y15: number, a7: [number];
27+
declare var x16: number, y16: number, z16: number, a8: [];
2828
declare var a9: (string | number | boolean)[];
29-
declare var x17: number, a10: (string | boolean)[];
30-
declare var x18: number, y18: string, a12: boolean[];
31-
declare var x19: number, y19: string, z19: boolean, a13: any[];
29+
declare var x17: number, a10: [string, boolean];
30+
declare var x18: number, y18: string, a12: [boolean];
31+
declare var x19: number, y19: string, z19: boolean, a13: [];

tests/baselines/reference/declarationEmitDestructuringArrayPattern4.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ var [...a5] = [1, 2, 3];
88

99
var [x14, ...a6] = [1, 2, 3];
1010
>x14 : number
11-
>a6 : number[]
11+
>a6 : [number, number]
1212
>[1, 2, 3] : [number, number, number]
1313
>1 : 1
1414
>2 : 2
@@ -17,7 +17,7 @@ var [x14, ...a6] = [1, 2, 3];
1717
var [x15, y15, ...a7] = [1, 2, 3];
1818
>x15 : number
1919
>y15 : number
20-
>a7 : number[]
20+
>a7 : [number]
2121
>[1, 2, 3] : [number, number, number]
2222
>1 : 1
2323
>2 : 2
@@ -27,7 +27,7 @@ var [x16, y16, z16, ...a8] = [1, 2, 3];
2727
>x16 : number
2828
>y16 : number
2929
>z16 : number
30-
>a8 : any[]
30+
>a8 : []
3131
>[1, 2, 3] : [number, number, number]
3232
>1 : 1
3333
>2 : 2
@@ -42,7 +42,7 @@ var [...a9] = [1, "hello", true];
4242

4343
var [x17, ...a10] = [1, "hello", true];
4444
>x17 : number
45-
>a10 : (string | boolean)[]
45+
>a10 : [string, boolean]
4646
>[1, "hello", true] : [number, string, boolean]
4747
>1 : 1
4848
>"hello" : "hello"
@@ -51,7 +51,7 @@ var [x17, ...a10] = [1, "hello", true];
5151
var [x18, y18, ...a12] = [1, "hello", true];
5252
>x18 : number
5353
>y18 : string
54-
>a12 : boolean[]
54+
>a12 : [boolean]
5555
>[1, "hello", true] : [number, string, boolean]
5656
>1 : 1
5757
>"hello" : "hello"
@@ -61,7 +61,7 @@ var [x19, y19, z19, ...a13] = [1, "hello", true];
6161
>x19 : number
6262
>y19 : string
6363
>z19 : boolean
64-
>a13 : any[]
64+
>a13 : []
6565
>[1, "hello", true] : [number, string, boolean]
6666
>1 : 1
6767
>"hello" : "hello"

tests/baselines/reference/declarationsAndAssignments.errors.txt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(106,5):
2323
Property 'x' is missing in type '{ y: false; }'.
2424
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,6): error TS2322: Type 'string' is not assignable to type 'number'.
2525
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): error TS2322: Type 'number' is not assignable to type 'string'.
26+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(158,16): error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number, number]'.
27+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(159,19): error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number]'.
28+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(160,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
29+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(176,16): error TS2403: Subsequent variable declarations must have the same type. Variable 'a1' must be of type '(string | boolean)[]', but here has type '[string, boolean]'.
30+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(177,19): error TS2403: Subsequent variable declarations must have the same type. Variable 'a2' must be of type 'boolean[]', but here has type '[boolean]'.
31+
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(178,22): error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
2632

2733

28-
==== tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts (22 errors) ====
34+
==== tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts (28 errors) ====
2935
function f0() {
3036
var [] = [1, "hello"];
3137
var [x] = [1, "hello"];
@@ -231,8 +237,14 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9):
231237
var a3: any[];
232238
var [...a] = [1, 2, 3];
233239
var [x, ...a] = [1, 2, 3];
240+
~
241+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number, number]'.
234242
var [x, y, ...a] = [1, 2, 3];
243+
~
244+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number]'.
235245
var [x, y, z, ...a3] = [1, 2, 3];
246+
~~
247+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
236248
[...a] = [1, 2, 3];
237249
[x, ...a] = [1, 2, 3];
238250
[x, y, ...a] = [1, 2, 3];
@@ -249,8 +261,14 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9):
249261
var a3: any[];
250262
var [...a0] = [1, "hello", true];
251263
var [x, ...a1] = [1, "hello", true];
264+
~~
265+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a1' must be of type '(string | boolean)[]', but here has type '[string, boolean]'.
252266
var [x, y, ...a2] = [1, "hello", true];
267+
~~
268+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a2' must be of type 'boolean[]', but here has type '[boolean]'.
253269
var [x, y, z, ...a3] = [1, "hello", true];
270+
~~
271+
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
254272
[...a0] = [1, "hello", true];
255273
[x, ...a1] = [1, "hello", true];
256274
[x, y, ...a2] = [1, "hello", true];

tests/baselines/reference/destructuringArrayBindingPatternAndAssignment1ES5.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
148148
> : undefined
149149
> : undefined
150150
> : undefined
151-
>c10 : (string | number)[]
151+
>c10 : [number, string]
152152
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
153153
>1 : 1
154154
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
159159
var [c11, c12, ...c13] = [1, 2, "string"];
160160
>c11 : number
161161
>c12 : number
162-
>c13 : string[]
162+
>c13 : [string]
163163
>[1, 2, "string"] : [number, number, string]
164164
>1 : 1
165165
>2 : 2

tests/baselines/reference/destructuringArrayBindingPatternAndAssignment1ES5iterable.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
148148
> : undefined
149149
> : undefined
150150
> : undefined
151-
>c10 : (string | number)[]
151+
>c10 : [number, string]
152152
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
153153
>1 : 1
154154
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
159159
var [c11, c12, ...c13] = [1, 2, "string"];
160160
>c11 : number
161161
>c12 : number
162-
>c13 : string[]
162+
>c13 : [string]
163163
>[1, 2, "string"] : [number, number, string]
164164
>1 : 1
165165
>2 : 2

tests/baselines/reference/destructuringArrayBindingPatternAndAssignment1ES6.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
148148
> : undefined
149149
> : undefined
150150
> : undefined
151-
>c10 : (string | number)[]
151+
>c10 : [number, string]
152152
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
153153
>1 : 1
154154
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
159159
var [c11, c12, ...c13] = [1, 2, "string"];
160160
>c11 : number
161161
>c12 : number
162-
>c13 : string[]
162+
>c13 : [string]
163163
>[1, 2, "string"] : [number, number, string]
164164
>1 : 1
165165
>2 : 2
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//// [destructuringTuple.ts]
2+
declare var tuple: [boolean, number, ...string[]];
3+
4+
const [a, b, c, ...rest] = tuple;
5+
6+
7+
//// [destructuringTuple.js]
8+
"use strict";
9+
var a = tuple[0], b = tuple[1], c = tuple[2], rest = tuple.slice(3);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/compiler/destructuringTuple.ts ===
2+
declare var tuple: [boolean, number, ...string[]];
3+
>tuple : Symbol(tuple, Decl(destructuringTuple.ts, 0, 11))
4+
5+
const [a, b, c, ...rest] = tuple;
6+
>a : Symbol(a, Decl(destructuringTuple.ts, 2, 7))
7+
>b : Symbol(b, Decl(destructuringTuple.ts, 2, 9))
8+
>c : Symbol(c, Decl(destructuringTuple.ts, 2, 12))
9+
>rest : Symbol(rest, Decl(destructuringTuple.ts, 2, 15))
10+
>tuple : Symbol(tuple, Decl(destructuringTuple.ts, 0, 11))
11+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/compiler/destructuringTuple.ts ===
2+
declare var tuple: [boolean, number, ...string[]];
3+
>tuple : [boolean, number, ...string[]]
4+
5+
const [a, b, c, ...rest] = tuple;
6+
>a : boolean
7+
>b : number
8+
>c : string
9+
>rest : string[]
10+
>tuple : [boolean, number, ...string[]]
11+

tests/baselines/reference/destructuringVariableDeclaration2.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ var [c1, c2, { c3: c4, c5 }, , ...c6] = [1, 2, { c3: 4, c5: 0 }]; // Error
5959
>c4 : number
6060
>c5 : number
6161
> : undefined
62-
>c6 : any[]
62+
>c6 : []
6363
>[1, 2, { c3: 4, c5: 0 }] : [number, number, { c3: number; c5: number; }, undefined?]
6464
>1 : 1
6565
>2 : 2

tests/baselines/reference/restElementMustBeLast.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
=== tests/cases/conformance/types/rest/restElementMustBeLast.ts ===
22
var [...a, x] = [1, 2, 3]; // Error, rest must be last element
3-
>a : number[]
3+
>a : [number, number, number]
44
>x : number
55
>[1, 2, 3] : [number, number, number]
66
>1 : 1
@@ -11,7 +11,7 @@ var [...a, x] = [1, 2, 3]; // Error, rest must be last element
1111
>[...a, x] = [1, 2, 3] : number[]
1212
>[...a, x] : number[]
1313
>...a : number
14-
>a : number[]
14+
>a : [number, number, number]
1515
>x : number
1616
>[1, 2, 3] : number[]
1717
>1 : 1

tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPattern.types

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ for (let [nameMA, [primarySkillA, secondarySkillA]] = ["trimmer", ["trimming", "
429429

430430
for (let [numberA3, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
431431
>numberA3 : number
432-
>robotAInfo : string[]
432+
>robotAInfo : [string, string]
433433
>robotA : [number, string, string]
434434
>i : number
435435
>0 : 0
@@ -448,7 +448,7 @@ for (let [numberA3, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
448448
}
449449
for (let [numberA3, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
450450
>numberA3 : number
451-
>robotAInfo : string[]
451+
>robotAInfo : [string, string]
452452
>getRobot() : [number, string, string]
453453
>getRobot : () => [number, string, string]
454454
>i : number
@@ -468,7 +468,7 @@ for (let [numberA3, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
468468
}
469469
for (let [numberA3, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < 1; i++) {
470470
>numberA3 : number
471-
>robotAInfo : string[]
471+
>robotAInfo : [string, string]
472472
>[2, "trimmer", "trimming"] : [number, string, string]
473473
>2 : 2
474474
>"trimmer" : "trimmer"
@@ -489,7 +489,7 @@ for (let [numberA3, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < 1; i
489489
>numberA3 : number
490490
}
491491
for (let [...multiRobotAInfo] = multiRobotA, i = 0; i < 1; i++) {
492-
>multiRobotAInfo : (string | [string, string])[]
492+
>multiRobotAInfo : [string, [string, string]]
493493
>multiRobotA : [string, [string, string]]
494494
>i : number
495495
>0 : 0
@@ -504,10 +504,10 @@ for (let [...multiRobotAInfo] = multiRobotA, i = 0; i < 1; i++) {
504504
>console.log : (msg: any) => void
505505
>console : { log(msg: any): void; }
506506
>log : (msg: any) => void
507-
>multiRobotAInfo : (string | [string, string])[]
507+
>multiRobotAInfo : [string, [string, string]]
508508
}
509509
for (let [...multiRobotAInfo] = getMultiRobot(), i = 0; i < 1; i++) {
510-
>multiRobotAInfo : (string | [string, string])[]
510+
>multiRobotAInfo : [string, [string, string]]
511511
>getMultiRobot() : [string, [string, string]]
512512
>getMultiRobot : () => [string, [string, string]]
513513
>i : number
@@ -523,7 +523,7 @@ for (let [...multiRobotAInfo] = getMultiRobot(), i = 0; i < 1; i++) {
523523
>console.log : (msg: any) => void
524524
>console : { log(msg: any): void; }
525525
>log : (msg: any) => void
526-
>multiRobotAInfo : (string | [string, string])[]
526+
>multiRobotAInfo : [string, [string, string]]
527527
}
528528
for (let [...multiRobotAInfo] = ["trimmer", ["trimming", "edging"]], i = 0; i < 1; i++) {
529529
>multiRobotAInfo : (string | string[])[]

tests/baselines/reference/sourceMapValidationDestructuringForArrayBindingPatternDefaultValues.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ for (let [numberA3 = -1, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
534534
>numberA3 : number
535535
>-1 : -1
536536
>1 : 1
537-
>robotAInfo : string[]
537+
>robotAInfo : [string, string]
538538
>robotA : [number, string, string]
539539
>i : number
540540
>0 : 0
@@ -555,7 +555,7 @@ for (let [numberA3 = -1, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
555555
>numberA3 : number
556556
>-1 : -1
557557
>1 : 1
558-
>robotAInfo : string[]
558+
>robotAInfo : [string, string]
559559
>getRobot() : [number, string, string]
560560
>getRobot : () => [number, string, string]
561561
>i : number
@@ -577,7 +577,7 @@ for (let [numberA3 = -1, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i <
577577
>numberA3 : number
578578
>-1 : -1
579579
>1 : 1
580-
>robotAInfo : string[]
580+
>robotAInfo : [string, string]
581581
>[2, "trimmer", "trimming"] : [number, string, string]
582582
>2 : 2
583583
>"trimmer" : "trimmer"

0 commit comments

Comments
 (0)