diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 259d805aeb0..b839dafd3e8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6011,14 +6011,38 @@ module ts { } let hasSpreadElement = false; let elementTypes: Type[] = []; + let inDestructuringPattern = isAssignmentTarget(node); for (let e of elements) { - let type = checkExpression(e, contextualMapper); - elementTypes.push(type); + if (inDestructuringPattern && e.kind === SyntaxKind.SpreadElementExpression) { + // Given the following situation: + // var c: {}; + // [...c] = ["", 0]; + // + // c is represented in the tree as a spread element in an array literal. + // But c really functions as a rest element, and its purpose is to provide + // a contextual type for the right hand side of the assignment. Therefore, + // instead of calling checkExpression on "...c", which will give an error + // if c is not iterable/array-like, we need to act as if we are trying to + // get the contextual element type from it. So we do something similar to + // getContextualTypeForElementExpression, which will crucially not error + // if there is no index type / iterated type. + let restArrayType = checkExpression((e).expression, contextualMapper); + let restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || + (languageVersion >= ScriptTarget.ES6 ? checkIteratedType(restArrayType, /*expressionForError*/ undefined) : undefined); + + if (restElementType) { + elementTypes.push(restElementType); + } + } + else { + let type = checkExpression(e, contextualMapper); + elementTypes.push(type); + } hasSpreadElement = hasSpreadElement || e.kind === SyntaxKind.SpreadElementExpression; } if (!hasSpreadElement) { let contextualType = getContextualType(node); - if (contextualType && contextualTypeIsTupleLikeType(contextualType) || isAssignmentTarget(node)) { + if (contextualType && contextualTypeIsTupleLikeType(contextualType) || inDestructuringPattern) { return createTupleType(elementTypes); } } diff --git a/tests/baselines/reference/nonIterableRestElement1.js b/tests/baselines/reference/nonIterableRestElement1.js new file mode 100644 index 00000000000..0b0d97aed2f --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement1.js @@ -0,0 +1,8 @@ +//// [nonIterableRestElement1.ts] +var c = {}; +[...c] = ["", 0]; + +//// [nonIterableRestElement1.js] +var c = {}; +_a = ["", 0], c = _a.slice(0); +var _a; diff --git a/tests/baselines/reference/nonIterableRestElement1.types b/tests/baselines/reference/nonIterableRestElement1.types new file mode 100644 index 00000000000..0731b042015 --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement1.types @@ -0,0 +1,12 @@ +=== tests/cases/conformance/es6/destructuring/nonIterableRestElement1.ts === +var c = {}; +>c : {} +>{} : {} + +[...c] = ["", 0]; +>[...c] = ["", 0] : (string | number)[] +>[...c] : {}[] +>...c : unknown +>c : {} +>["", 0] : (string | number)[] + diff --git a/tests/baselines/reference/nonIterableRestElement2.js b/tests/baselines/reference/nonIterableRestElement2.js new file mode 100644 index 00000000000..edeef32e188 --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement2.js @@ -0,0 +1,7 @@ +//// [nonIterableRestElement2.ts] +var c = {}; +[...c] = ["", 0]; + +//// [nonIterableRestElement2.js] +var c = {}; +[...c] = ["", 0]; diff --git a/tests/baselines/reference/nonIterableRestElement2.types b/tests/baselines/reference/nonIterableRestElement2.types new file mode 100644 index 00000000000..ca9e768b7e2 --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement2.types @@ -0,0 +1,12 @@ +=== tests/cases/conformance/es6/destructuring/nonIterableRestElement2.ts === +var c = {}; +>c : {} +>{} : {} + +[...c] = ["", 0]; +>[...c] = ["", 0] : (string | number)[] +>[...c] : {}[] +>...c : any +>c : {} +>["", 0] : (string | number)[] + diff --git a/tests/baselines/reference/nonIterableRestElement3.errors.txt b/tests/baselines/reference/nonIterableRestElement3.errors.txt new file mode 100644 index 00000000000..2c44aba661e --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement3.errors.txt @@ -0,0 +1,10 @@ +tests/cases/conformance/es6/destructuring/nonIterableRestElement3.ts(2,5): error TS2322: Type '(string | number)[]' is not assignable to type '{ bogus: number; }'. + Property 'bogus' is missing in type '(string | number)[]'. + + +==== tests/cases/conformance/es6/destructuring/nonIterableRestElement3.ts (1 errors) ==== + var c = { bogus: 0 }; + [...c] = ["", 0]; + ~ +!!! error TS2322: Type '(string | number)[]' is not assignable to type '{ bogus: number; }'. +!!! error TS2322: Property 'bogus' is missing in type '(string | number)[]'. \ No newline at end of file diff --git a/tests/baselines/reference/nonIterableRestElement3.js b/tests/baselines/reference/nonIterableRestElement3.js new file mode 100644 index 00000000000..2901699a878 --- /dev/null +++ b/tests/baselines/reference/nonIterableRestElement3.js @@ -0,0 +1,8 @@ +//// [nonIterableRestElement3.ts] +var c = { bogus: 0 }; +[...c] = ["", 0]; + +//// [nonIterableRestElement3.js] +var c = { bogus: 0 }; +_a = ["", 0], c = _a.slice(0); +var _a; diff --git a/tests/baselines/reference/restElementWithAssignmentPattern2.errors.txt b/tests/baselines/reference/restElementWithAssignmentPattern2.errors.txt index 4b620b19a7a..f9b11b5694c 100644 --- a/tests/baselines/reference/restElementWithAssignmentPattern2.errors.txt +++ b/tests/baselines/reference/restElementWithAssignmentPattern2.errors.txt @@ -1,14 +1,11 @@ -tests/cases/conformance/es6/destructuring/restElementWithAssignmentPattern2.ts(2,5): error TS2461: Type '{ 0: string; b: number; }' is not an array type. tests/cases/conformance/es6/destructuring/restElementWithAssignmentPattern2.ts(2,10): error TS2322: Type 'string | number' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'. tests/cases/conformance/es6/destructuring/restElementWithAssignmentPattern2.ts(2,18): error TS2459: Type '(string | number)[]' has no property 'b' and no string index signature. -==== tests/cases/conformance/es6/destructuring/restElementWithAssignmentPattern2.ts (3 errors) ==== +==== tests/cases/conformance/es6/destructuring/restElementWithAssignmentPattern2.ts (2 errors) ==== var a: string, b: number; [...{ 0: a = "", b }] = ["", 1]; - ~~~~~~~~~~~~~~~~ -!!! error TS2461: Type '{ 0: string; b: number; }' is not an array type. ~ !!! error TS2322: Type 'string | number' is not assignable to type 'string'. !!! error TS2322: Type 'number' is not assignable to type 'string'. diff --git a/tests/cases/conformance/es6/destructuring/nonIterableRestElement1.ts b/tests/cases/conformance/es6/destructuring/nonIterableRestElement1.ts new file mode 100644 index 00000000000..3923e7e538e --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/nonIterableRestElement1.ts @@ -0,0 +1,2 @@ +var c = {}; +[...c] = ["", 0]; \ No newline at end of file diff --git a/tests/cases/conformance/es6/destructuring/nonIterableRestElement2.ts b/tests/cases/conformance/es6/destructuring/nonIterableRestElement2.ts new file mode 100644 index 00000000000..2bede0e7f57 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/nonIterableRestElement2.ts @@ -0,0 +1,3 @@ +//@target: ES6 +var c = {}; +[...c] = ["", 0]; \ No newline at end of file diff --git a/tests/cases/conformance/es6/destructuring/nonIterableRestElement3.ts b/tests/cases/conformance/es6/destructuring/nonIterableRestElement3.ts new file mode 100644 index 00000000000..cffac92ebf4 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/nonIterableRestElement3.ts @@ -0,0 +1,2 @@ +var c = { bogus: 0 }; +[...c] = ["", 0]; \ No newline at end of file