Infer array rest as tuple if possible

Fixes: #26007
This commit is contained in:
Klaus Meinhardt
2018-07-31 00:11:05 +02:00
parent 0923771606
commit 090326be6a
24 changed files with 124 additions and 56 deletions

View File

@@ -4555,11 +4555,11 @@ namespace ts {
const elementType = checkIteratedTypeOrElementType(parentType, pattern, /*allowStringInput*/ false, /*allowAsyncIterables*/ false);
const index = pattern.elements.indexOf(declaration);
if (declaration.dotDotDotToken) {
// If the parent is a tuple type, the rest element has an array type with a union of the
// If the parent is a tuple type, the rest element has a tuple type of the
// remaining tuple element types. Otherwise, the rest element has an array type with same
// element type as the parent type.
type = isTupleType(parentType) ?
getArrayLiteralType((parentType.typeArguments || emptyArray).slice(index, getTypeReferenceArity(parentType))) :
sliceTupleType(parentType, index) :
createArrayType(elementType);
}
else {
@@ -8526,6 +8526,20 @@ namespace ts {
return links.resolvedType;
}
function sliceTupleType(type: TupleTypeReference, index: number) {
const tuple = type.target;
if (tuple.hasRestElement) {
// don't slice off rest element
index = Math.min(index, getTypeReferenceArity(type) - 1);
}
return createTupleType(
(type.typeArguments || emptyArray).slice(index),
Math.max(0, tuple.minLength - index),
tuple.hasRestElement,
tuple.associatedNames && tuple.associatedNames.slice(index),
);
}
function getTypeFromOptionalTypeNode(node: OptionalTypeNode): Type {
const type = getTypeFromTypeNode(node.type);
return strictNullChecks ? getOptionalType(type) : type;

View File

@@ -8,7 +8,7 @@ function bar([x, z, ...w]) { }
function foo([x, ...y] = [1, "string", true]) { }
>foo : ([x, ...y]?: [number, string, boolean]) => void
>x : number
>y : (string | boolean)[]
>y : [string, boolean]
>[1, "string", true] : [number, string, boolean]
>1 : 1
>"string" : "string"

View File

@@ -22,10 +22,10 @@ var _f = [1, "hello", true], x19 = _f[0], y19 = _f[1], z19 = _f[2], a13 = _f.sli
//// [declarationEmitDestructuringArrayPattern4.d.ts]
declare var a5: number[];
declare var x14: number, a6: number[];
declare var x15: number, y15: number, a7: number[];
declare var x16: number, y16: number, z16: number, a8: any[];
declare var x14: number, a6: [number, number];
declare var x15: number, y15: number, a7: [number];
declare var x16: number, y16: number, z16: number, a8: [];
declare var a9: (string | number | boolean)[];
declare var x17: number, a10: (string | boolean)[];
declare var x18: number, y18: string, a12: boolean[];
declare var x19: number, y19: string, z19: boolean, a13: any[];
declare var x17: number, a10: [string, boolean];
declare var x18: number, y18: string, a12: [boolean];
declare var x19: number, y19: string, z19: boolean, a13: [];

View File

@@ -8,7 +8,7 @@ var [...a5] = [1, 2, 3];
var [x14, ...a6] = [1, 2, 3];
>x14 : number
>a6 : number[]
>a6 : [number, number]
>[1, 2, 3] : [number, number, number]
>1 : 1
>2 : 2
@@ -17,7 +17,7 @@ var [x14, ...a6] = [1, 2, 3];
var [x15, y15, ...a7] = [1, 2, 3];
>x15 : number
>y15 : number
>a7 : number[]
>a7 : [number]
>[1, 2, 3] : [number, number, number]
>1 : 1
>2 : 2
@@ -27,7 +27,7 @@ var [x16, y16, z16, ...a8] = [1, 2, 3];
>x16 : number
>y16 : number
>z16 : number
>a8 : any[]
>a8 : []
>[1, 2, 3] : [number, number, number]
>1 : 1
>2 : 2
@@ -42,7 +42,7 @@ var [...a9] = [1, "hello", true];
var [x17, ...a10] = [1, "hello", true];
>x17 : number
>a10 : (string | boolean)[]
>a10 : [string, boolean]
>[1, "hello", true] : [number, string, boolean]
>1 : 1
>"hello" : "hello"
@@ -51,7 +51,7 @@ var [x17, ...a10] = [1, "hello", true];
var [x18, y18, ...a12] = [1, "hello", true];
>x18 : number
>y18 : string
>a12 : boolean[]
>a12 : [boolean]
>[1, "hello", true] : [number, string, boolean]
>1 : 1
>"hello" : "hello"
@@ -61,7 +61,7 @@ var [x19, y19, z19, ...a13] = [1, "hello", true];
>x19 : number
>y19 : string
>z19 : boolean
>a13 : any[]
>a13 : []
>[1, "hello", true] : [number, string, boolean]
>1 : 1
>"hello" : "hello"

View File

@@ -23,9 +23,15 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(106,5):
Property 'x' is missing in type '{ y: false; }'.
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,6): error TS2322: Type 'string' is not assignable to type 'number'.
tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): error TS2322: Type 'number' is not assignable to type 'string'.
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]'.
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]'.
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 '[]'.
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]'.
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]'.
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 '[]'.
==== tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts (22 errors) ====
==== tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts (28 errors) ====
function f0() {
var [] = [1, "hello"];
var [x] = [1, "hello"];
@@ -231,8 +237,14 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9):
var a3: any[];
var [...a] = [1, 2, 3];
var [x, ...a] = [1, 2, 3];
~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number, number]'.
var [x, y, ...a] = [1, 2, 3];
~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a' must be of type 'number[]', but here has type '[number]'.
var [x, y, z, ...a3] = [1, 2, 3];
~~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
[...a] = [1, 2, 3];
[x, ...a] = [1, 2, 3];
[x, y, ...a] = [1, 2, 3];
@@ -249,8 +261,14 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9):
var a3: any[];
var [...a0] = [1, "hello", true];
var [x, ...a1] = [1, "hello", true];
~~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a1' must be of type '(string | boolean)[]', but here has type '[string, boolean]'.
var [x, y, ...a2] = [1, "hello", true];
~~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a2' must be of type 'boolean[]', but here has type '[boolean]'.
var [x, y, z, ...a3] = [1, "hello", true];
~~
!!! error TS2403: Subsequent variable declarations must have the same type. Variable 'a3' must be of type 'any[]', but here has type '[]'.
[...a0] = [1, "hello", true];
[x, ...a1] = [1, "hello", true];
[x, y, ...a2] = [1, "hello", true];

View File

@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
> : undefined
> : undefined
> : undefined
>c10 : (string | number)[]
>c10 : [number, string]
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
>1 : 1
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
var [c11, c12, ...c13] = [1, 2, "string"];
>c11 : number
>c12 : number
>c13 : string[]
>c13 : [string]
>[1, 2, "string"] : [number, number, string]
>1 : 1
>2 : 2

View File

@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
> : undefined
> : undefined
> : undefined
>c10 : (string | number)[]
>c10 : [number, string]
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
>1 : 1
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
var [c11, c12, ...c13] = [1, 2, "string"];
>c11 : number
>c12 : number
>c13 : string[]
>c13 : [string]
>[1, 2, "string"] : [number, number, string]
>1 : 1
>2 : 2

View File

@@ -148,7 +148,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
> : undefined
> : undefined
> : undefined
>c10 : (string | number)[]
>c10 : [number, string]
>[1, 2, 3, 4, "hello"] : [number, number, number, number, string]
>1 : 1
>2 : 2
@@ -159,7 +159,7 @@ var [,,,...c10] = [1, 2, 3, 4, "hello"];
var [c11, c12, ...c13] = [1, 2, "string"];
>c11 : number
>c12 : number
>c13 : string[]
>c13 : [string]
>[1, 2, "string"] : [number, number, string]
>1 : 1
>2 : 2

View File

@@ -0,0 +1,9 @@
//// [destructuringTuple.ts]
declare var tuple: [boolean, number, ...string[]];
const [a, b, c, ...rest] = tuple;
//// [destructuringTuple.js]
"use strict";
var a = tuple[0], b = tuple[1], c = tuple[2], rest = tuple.slice(3);

View File

@@ -0,0 +1,11 @@
=== tests/cases/compiler/destructuringTuple.ts ===
declare var tuple: [boolean, number, ...string[]];
>tuple : Symbol(tuple, Decl(destructuringTuple.ts, 0, 11))
const [a, b, c, ...rest] = tuple;
>a : Symbol(a, Decl(destructuringTuple.ts, 2, 7))
>b : Symbol(b, Decl(destructuringTuple.ts, 2, 9))
>c : Symbol(c, Decl(destructuringTuple.ts, 2, 12))
>rest : Symbol(rest, Decl(destructuringTuple.ts, 2, 15))
>tuple : Symbol(tuple, Decl(destructuringTuple.ts, 0, 11))

View File

@@ -0,0 +1,11 @@
=== tests/cases/compiler/destructuringTuple.ts ===
declare var tuple: [boolean, number, ...string[]];
>tuple : [boolean, number, ...string[]]
const [a, b, c, ...rest] = tuple;
>a : boolean
>b : number
>c : string
>rest : string[]
>tuple : [boolean, number, ...string[]]

View File

@@ -59,7 +59,7 @@ var [c1, c2, { c3: c4, c5 }, , ...c6] = [1, 2, { c3: 4, c5: 0 }]; // Error
>c4 : number
>c5 : number
> : undefined
>c6 : any[]
>c6 : []
>[1, 2, { c3: 4, c5: 0 }] : [number, number, { c3: number; c5: number; }, undefined?]
>1 : 1
>2 : 2

View File

@@ -1,6 +1,6 @@
=== tests/cases/conformance/types/rest/restElementMustBeLast.ts ===
var [...a, x] = [1, 2, 3]; // Error, rest must be last element
>a : number[]
>a : [number, number, number]
>x : number
>[1, 2, 3] : [number, number, number]
>1 : 1
@@ -11,7 +11,7 @@ var [...a, x] = [1, 2, 3]; // Error, rest must be last element
>[...a, x] = [1, 2, 3] : number[]
>[...a, x] : number[]
>...a : number
>a : number[]
>a : [number, number, number]
>x : number
>[1, 2, 3] : number[]
>1 : 1

View File

@@ -429,7 +429,7 @@ for (let [nameMA, [primarySkillA, secondarySkillA]] = ["trimmer", ["trimming", "
for (let [numberA3, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>robotA : [number, string, string]
>i : number
>0 : 0
@@ -448,7 +448,7 @@ for (let [numberA3, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
}
for (let [numberA3, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>getRobot() : [number, string, string]
>getRobot : () => [number, string, string]
>i : number
@@ -468,7 +468,7 @@ for (let [numberA3, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
}
for (let [numberA3, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < 1; i++) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>[2, "trimmer", "trimming"] : [number, string, string]
>2 : 2
>"trimmer" : "trimmer"
@@ -489,7 +489,7 @@ for (let [numberA3, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i < 1; i
>numberA3 : number
}
for (let [...multiRobotAInfo] = multiRobotA, i = 0; i < 1; i++) {
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>multiRobotA : [string, [string, string]]
>i : number
>0 : 0
@@ -504,10 +504,10 @@ for (let [...multiRobotAInfo] = multiRobotA, i = 0; i < 1; i++) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}
for (let [...multiRobotAInfo] = getMultiRobot(), i = 0; i < 1; i++) {
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>getMultiRobot() : [string, [string, string]]
>getMultiRobot : () => [string, [string, string]]
>i : number
@@ -523,7 +523,7 @@ for (let [...multiRobotAInfo] = getMultiRobot(), i = 0; i < 1; i++) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}
for (let [...multiRobotAInfo] = ["trimmer", ["trimming", "edging"]], i = 0; i < 1; i++) {
>multiRobotAInfo : (string | string[])[]

View File

@@ -534,7 +534,7 @@ for (let [numberA3 = -1, ...robotAInfo] = robotA, i = 0; i < 1; i++) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>robotA : [number, string, string]
>i : number
>0 : 0
@@ -555,7 +555,7 @@ for (let [numberA3 = -1, ...robotAInfo] = getRobot(), i = 0; i < 1; i++) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>getRobot() : [number, string, string]
>getRobot : () => [number, string, string]
>i : number
@@ -577,7 +577,7 @@ for (let [numberA3 = -1, ...robotAInfo] = [2, "trimmer", "trimming"], i = 0; i <
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>[2, "trimmer", "trimming"] : [number, string, string]
>2 : 2
>"trimmer" : "trimmer"

View File

@@ -314,7 +314,7 @@ for (let [nameMA, [primarySkillA, secondarySkillA]] of [multiRobotA, multiRobotB
for (let [numberA3, ...robotAInfo] of robots) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>robots : [number, string, string][]
console.log(numberA3);
@@ -326,7 +326,7 @@ for (let [numberA3, ...robotAInfo] of robots) {
}
for (let [numberA3, ...robotAInfo] of getRobots()) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>getRobots() : [number, string, string][]
>getRobots : () => [number, string, string][]
@@ -339,7 +339,7 @@ for (let [numberA3, ...robotAInfo] of getRobots()) {
}
for (let [numberA3, ...robotAInfo] of [robotA, robotB]) {
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>[robotA, robotB] : [number, string, string][]
>robotA : [number, string, string]
>robotB : [number, string, string]
@@ -352,7 +352,7 @@ for (let [numberA3, ...robotAInfo] of [robotA, robotB]) {
>numberA3 : number
}
for (let [...multiRobotAInfo] of multiRobots) {
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>multiRobots : [string, [string, string]][]
console.log(multiRobotAInfo);
@@ -360,10 +360,10 @@ for (let [...multiRobotAInfo] of multiRobots) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}
for (let [...multiRobotAInfo] of getMultiRobots()) {
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>getMultiRobots() : [string, [string, string]][]
>getMultiRobots : () => [string, [string, string]][]
@@ -372,10 +372,10 @@ for (let [...multiRobotAInfo] of getMultiRobots()) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}
for (let [...multiRobotAInfo] of [multiRobotA, multiRobotB]) {
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>[multiRobotA, multiRobotB] : [string, [string, string]][]
>multiRobotA : [string, [string, string]]
>multiRobotB : [string, [string, string]]
@@ -385,5 +385,5 @@ for (let [...multiRobotAInfo] of [multiRobotA, multiRobotB]) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}

View File

@@ -409,7 +409,7 @@ for (let [numberA3 = -1, ...robotAInfo] of robots) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>robots : [number, string, string][]
console.log(numberA3);
@@ -423,7 +423,7 @@ for (let [numberA3 = -1, ...robotAInfo] of getRobots()) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>getRobots() : [number, string, string][]
>getRobots : () => [number, string, string][]
@@ -438,7 +438,7 @@ for (let [numberA3 = -1, ...robotAInfo] of [robotA, robotB]) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>[robotA, robotB] : [number, string, string][]
>robotA : [number, string, string]
>robotB : [number, string, string]

View File

@@ -62,7 +62,7 @@ function foo3([numberA2, nameA2, skillA2]: Robot) {
function foo4([numberA3, ...robotAInfo]: Robot) {
>foo4 : ([numberA3, ...robotAInfo]: [number, string, string]) => void
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>Robot : [number, string, string]
console.log(robotAInfo);
@@ -70,7 +70,7 @@ function foo4([numberA3, ...robotAInfo]: Robot) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>robotAInfo : string[]
>robotAInfo : [string, string]
}
foo1(robotA);

View File

@@ -62,7 +62,7 @@ function foo3([nameMA, [primarySkillA, secondarySkillA]]: Robot) {
function foo4([...multiRobotAInfo]: Robot) {
>foo4 : ([...multiRobotAInfo]: [string, [string, string]]) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>Robot : [string, [string, string]]
console.log(multiRobotAInfo);
@@ -70,7 +70,7 @@ function foo4([...multiRobotAInfo]: Robot) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
}
foo1(robotA);

View File

@@ -86,7 +86,7 @@ function foo4([numberA3 = -1, ...robotAInfo]: Robot = [-1, "name", "skill"]) {
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>Robot : [number, string, string]
>[-1, "name", "skill"] : [number, string, string]
>-1 : -1
@@ -99,7 +99,7 @@ function foo4([numberA3 = -1, ...robotAInfo]: Robot = [-1, "name", "skill"]) {
>console.log : (msg: any) => void
>console : { log(msg: any): void; }
>log : (msg: any) => void
>robotAInfo : string[]
>robotAInfo : [string, string]
}
foo1(robotA);

View File

@@ -59,7 +59,7 @@ let [numberC, nameC, skillC] = [3, "edging", "Trimming edges"];
let [numberA3, ...robotAInfo] = robotA;
>numberA3 : number
>robotAInfo : string[]
>robotAInfo : [string, string]
>robotA : [number, string, string]
if (nameA == nameA2) {

View File

@@ -61,7 +61,7 @@ let [nameMC2, [primarySkillC, secondarySkillC]] = ["roomba", ["vaccum", "mopping
>"mopping" : "mopping"
let [...multiRobotAInfo] = multiRobotA;
>multiRobotAInfo : (string | [string, string])[]
>multiRobotAInfo : [string, [string, string]]
>multiRobotA : [string, [string, string]]
if (nameMB == nameMA) {

View File

@@ -73,7 +73,7 @@ let [numberA3 = -1, ...robotAInfo] = robotA;
>numberA3 : number
>-1 : -1
>1 : 1
>robotAInfo : string[]
>robotAInfo : [string, string]
>robotA : [number, string, string]
if (nameA == nameA2) {

View File

@@ -0,0 +1,5 @@
// @strict: true
declare var tuple: [boolean, number, ...string[]];
const [a, b, c, ...rest] = tuple;