From f8150d3734759a0befc54a97074305efca6d86f9 Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Thu, 26 Feb 2015 15:32:37 -0800 Subject: [PATCH] Support assignment patterns in 'for...of' statements --- src/compiler/checker.ts | 30 ++++++++++++------- tests/baselines/reference/for-of45.js | 15 ++++++++++ tests/baselines/reference/for-of45.types | 26 ++++++++++++++++ tests/baselines/reference/for-of46.errors.txt | 15 ++++++++++ tests/baselines/reference/for-of46.js | 15 ++++++++++ tests/baselines/reference/for-of47.errors.txt | 13 ++++++++ tests/baselines/reference/for-of47.js | 20 +++++++++++++ .../es6/for-ofStatements/for-of45.ts | 7 +++++ .../es6/for-ofStatements/for-of46.ts | 7 +++++ .../es6/for-ofStatements/for-of47.ts | 8 +++++ 10 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/for-of45.js create mode 100644 tests/baselines/reference/for-of45.types create mode 100644 tests/baselines/reference/for-of46.errors.txt create mode 100644 tests/baselines/reference/for-of46.js create mode 100644 tests/baselines/reference/for-of47.errors.txt create mode 100644 tests/baselines/reference/for-of47.js create mode 100644 tests/cases/conformance/es6/for-ofStatements/for-of45.ts create mode 100644 tests/cases/conformance/es6/for-ofStatements/for-of46.ts create mode 100644 tests/cases/conformance/es6/for-ofStatements/for-of47.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index be59b41fbe..ae1e9a398a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7411,7 +7411,7 @@ module ts { return rightType; } - // Return type is true if there was no error, false if there was an error. + // Return true if there was no error, false if there was an error. function checkForDisallowedESSymbolOperand(operator: SyntaxKind): boolean { var offendingSymbolOperand = someConstituentTypeHasKind(leftType, TypeFlags.ESSymbol) ? node.left : @@ -8758,7 +8758,7 @@ module ts { grammarErrorOnFirstToken(node, Diagnostics.for_of_statements_are_only_available_when_targeting_ECMAScript_6_or_higher); return; } - + checkGrammarForInOrForOfStatement(node) // Check the LHS and RHS @@ -8769,17 +8769,27 @@ module ts { } else { var varExpr = node.initializer; - var leftType = checkExpression(varExpr); - checkReferenceExpression(varExpr, Diagnostics.Invalid_left_hand_side_in_for_of_statement, Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_be_a_previously_defined_constant); var rightType = checkExpression(node.expression); var iteratedType = getIteratedType(rightType, node.expression); - // iteratedType will be undefined if the rightType was missing properties/signatures - // required to get it's iteratedType (like [Symbol.iterator] or next). This may be - // because we accessed properties from anyType, or it may have led to an error inside - // getIteratedType. - if (iteratedType) { - checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined); + // There may be a destructuring assignment on the left side + if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { + // iteratedType may be undefined. In this case, we still want to check the structure of + // varExpr, in particular making sure it's a valid LeftHandSideExpression. But we'd like + // to short circuit the type relation checking as much as possible, so we pass the unknownType. + checkDestructuringAssignment(varExpr, iteratedType || unknownType); + } + else { + var leftType = checkExpression(varExpr); + checkReferenceExpression(varExpr, Diagnostics.Invalid_left_hand_side_in_for_of_statement, Diagnostics.The_left_hand_side_of_a_for_of_statement_cannot_be_a_previously_defined_constant); + + // iteratedType will be undefined if the rightType was missing properties/signatures + // required to get it's iteratedType (like [Symbol.iterator] or next). This may be + // because we accessed properties from anyType, or it may have led to an error inside + // getIteratedType. + if (iteratedType) { + checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined); + } } } diff --git a/tests/baselines/reference/for-of45.js b/tests/baselines/reference/for-of45.js new file mode 100644 index 0000000000..1222b2dcdd --- /dev/null +++ b/tests/baselines/reference/for-of45.js @@ -0,0 +1,15 @@ +//// [for-of45.ts] +var k: string, v: boolean; +var map = new Map([["", true]]); +for ([k = "", v = false] of map) { + k; + v; +} + +//// [for-of45.js] +var k, v; +var map = new Map([["", true]]); +for ([k = "", v = false] of map) { + k; + v; +} diff --git a/tests/baselines/reference/for-of45.types b/tests/baselines/reference/for-of45.types new file mode 100644 index 0000000000..8ac4b9fa7e --- /dev/null +++ b/tests/baselines/reference/for-of45.types @@ -0,0 +1,26 @@ +=== tests/cases/conformance/es6/for-ofStatements/for-of45.ts === +var k: string, v: boolean; +>k : string +>v : boolean + +var map = new Map([["", true]]); +>map : Map +>new Map([["", true]]) : Map +>Map : MapConstructor +>[["", true]] : [string, boolean][] +>["", true] : [string, boolean] + +for ([k = "", v = false] of map) { +>[k = "", v = false] : (string | boolean)[] +>k = "" : string +>k : string +>v = false : boolean +>v : boolean +>map : Map + + k; +>k : string + + v; +>v : boolean +} diff --git a/tests/baselines/reference/for-of46.errors.txt b/tests/baselines/reference/for-of46.errors.txt new file mode 100644 index 0000000000..13685b122c --- /dev/null +++ b/tests/baselines/reference/for-of46.errors.txt @@ -0,0 +1,15 @@ +tests/cases/conformance/es6/for-ofStatements/for-of46.ts(3,7): error TS2322: Type 'boolean' is not assignable to type 'string'. +tests/cases/conformance/es6/for-ofStatements/for-of46.ts(3,18): error TS2322: Type 'string' is not assignable to type 'boolean'. + + +==== tests/cases/conformance/es6/for-ofStatements/for-of46.ts (2 errors) ==== + var k: string, v: boolean; + var map = new Map([["", true]]); + for ([k = false, v = ""] of map) { + ~ +!!! error TS2322: Type 'boolean' is not assignable to type 'string'. + ~ +!!! error TS2322: Type 'string' is not assignable to type 'boolean'. + k; + v; + } \ No newline at end of file diff --git a/tests/baselines/reference/for-of46.js b/tests/baselines/reference/for-of46.js new file mode 100644 index 0000000000..2ea15936c5 --- /dev/null +++ b/tests/baselines/reference/for-of46.js @@ -0,0 +1,15 @@ +//// [for-of46.ts] +var k: string, v: boolean; +var map = new Map([["", true]]); +for ([k = false, v = ""] of map) { + k; + v; +} + +//// [for-of46.js] +var k, v; +var map = new Map([["", true]]); +for ([k = false, v = ""] of map) { + k; + v; +} diff --git a/tests/baselines/reference/for-of47.errors.txt b/tests/baselines/reference/for-of47.errors.txt new file mode 100644 index 0000000000..241dc107ca --- /dev/null +++ b/tests/baselines/reference/for-of47.errors.txt @@ -0,0 +1,13 @@ +tests/cases/conformance/es6/for-ofStatements/for-of47.ts(4,13): error TS2322: Type 'boolean' is not assignable to type 'number'. + + +==== tests/cases/conformance/es6/for-ofStatements/for-of47.ts (1 errors) ==== + var x: string, y: number; + var array = [{ x: "", y: true }] + enum E { x } + for ({x, y: y = E.x} of array) { + ~ +!!! error TS2322: Type 'boolean' is not assignable to type 'number'. + x; + y; + } \ No newline at end of file diff --git a/tests/baselines/reference/for-of47.js b/tests/baselines/reference/for-of47.js new file mode 100644 index 0000000000..b2de46d677 --- /dev/null +++ b/tests/baselines/reference/for-of47.js @@ -0,0 +1,20 @@ +//// [for-of47.ts] +var x: string, y: number; +var array = [{ x: "", y: true }] +enum E { x } +for ({x, y: y = E.x} of array) { + x; + y; +} + +//// [for-of47.js] +var x, y; +var array = [{ x: "", y: true }]; +var E; +(function (E) { + E[E["x"] = 0] = "x"; +})(E || (E = {})); +for ({ x, y: y = 0 /* x */ } of array) { + x; + y; +} diff --git a/tests/cases/conformance/es6/for-ofStatements/for-of45.ts b/tests/cases/conformance/es6/for-ofStatements/for-of45.ts new file mode 100644 index 0000000000..f347069ab1 --- /dev/null +++ b/tests/cases/conformance/es6/for-ofStatements/for-of45.ts @@ -0,0 +1,7 @@ +//@target: ES6 +var k: string, v: boolean; +var map = new Map([["", true]]); +for ([k = "", v = false] of map) { + k; + v; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/for-ofStatements/for-of46.ts b/tests/cases/conformance/es6/for-ofStatements/for-of46.ts new file mode 100644 index 0000000000..a08791068b --- /dev/null +++ b/tests/cases/conformance/es6/for-ofStatements/for-of46.ts @@ -0,0 +1,7 @@ +//@target: ES6 +var k: string, v: boolean; +var map = new Map([["", true]]); +for ([k = false, v = ""] of map) { + k; + v; +} \ No newline at end of file diff --git a/tests/cases/conformance/es6/for-ofStatements/for-of47.ts b/tests/cases/conformance/es6/for-ofStatements/for-of47.ts new file mode 100644 index 0000000000..bd21a0db38 --- /dev/null +++ b/tests/cases/conformance/es6/for-ofStatements/for-of47.ts @@ -0,0 +1,8 @@ +//@target: ES6 +var x: string, y: number; +var array = [{ x: "", y: true }] +enum E { x } +for ({x, y: y = E.x} of array) { + x; + y; +} \ No newline at end of file