Skip to content

Commit

Permalink
Introduce checkElementTypeOfArrayOrString for downlevel for..of type …
Browse files Browse the repository at this point in the history
…checking
  • Loading branch information
JsonFreeman committed Mar 11, 2015
1 parent 5b46f5f commit 61cd2a7
Show file tree
Hide file tree
Showing 39 changed files with 685 additions and 954 deletions.
67 changes: 51 additions & 16 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1869,7 +1869,11 @@ module ts {
return anyType;
}
if (declaration.parent.parent.kind === SyntaxKind.ForOfStatement) {

This comment has been minimized.

Copy link
@yuit

yuit Mar 12, 2015

Contributor

since you may use declaration.parent.parent, I would prefer var forOf = declaration.parent.parent

This comment has been minimized.

Copy link
@JsonFreeman

JsonFreeman Mar 12, 2015

Author Contributor

Well, I can't do it outside the if, because at that point I don't know if it's a ForOfStatement. I will keep it this way because I will only use the parent.parent once, after I know what it is.

return getTypeForVariableDeclarationInForOfStatement(<ForOfStatement>declaration.parent.parent);
// checkRightHandSideOfForOf will return undefined if the for-of expression type was
// missing properties/signatures required to get its 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.
return checkRightHandSideOfForOf((<ForOfStatement>declaration.parent.parent).expression) || anyType;
}
if (isBindingPattern(declaration.parent)) {
return getTypeForBindingElement(<BindingElement>declaration);
Expand Down Expand Up @@ -8781,16 +8785,15 @@ module ts {

// Check the LHS and RHS
// If the LHS is a declaration, just check it as a variable declaration, which will in turn check the RHS
// via getTypeForVariableDeclarationInForOfStatement.
// via checkRightHandSideOfForOf.
// If the LHS is an expression, check the LHS, as a destructuring assignment or as a reference.
// Then check that the RHS is assignable to it.
if (node.initializer.kind === SyntaxKind.VariableDeclarationList) {
checkForInOrForOfVariableDeclaration(node);
}
else {
var varExpr = <Expression>node.initializer;
var rightType = checkExpression(node.expression);
var iteratedType = checkIteratedType(rightType, node.expression);
var iteratedType = checkRightHandSideOfForOf(node.expression);

// There may be a destructuring assignment on the left side
if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) {
Expand Down Expand Up @@ -8872,18 +8875,11 @@ module ts {
}
}

function getTypeForVariableDeclarationInForOfStatement(forOfStatement: ForOfStatement): Type {
// Temporarily return 'any' below ES6
if (languageVersion < ScriptTarget.ES6) {
return anyType;
}

// iteratedType will be undefined if the for-of expression type was missing properties/signatures
// required to get its 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.
var expressionType = getTypeOfExpression(forOfStatement.expression);
return checkIteratedType(expressionType, forOfStatement.expression) || anyType;
function checkRightHandSideOfForOf(rhsExpression: Expression): Type {
var expressionType = getTypeOfExpression(rhsExpression);
return languageVersion >= ScriptTarget.ES6
? checkIteratedType(expressionType, rhsExpression)
: checkElementTypeOfArrayOrString(expressionType, rhsExpression);
}

/**
Expand Down Expand Up @@ -8982,6 +8978,45 @@ module ts {
}
}

function checkElementTypeOfArrayOrString(arrayOrStringType: Type, expressionForError: Expression): Type {
Debug.assert(languageVersion < ScriptTarget.ES6);
var isJustString = allConstituentTypesHaveKind(arrayOrStringType, TypeFlags.StringLike);

// Check isJustString because removeTypesFromUnionType will only remove types if it doesn't result
// in an emptyObjectType. In this case, we actually do want the emptyObjectType.
var arrayType = isJustString ? emptyObjectType : removeTypesFromUnionType(arrayOrStringType, TypeFlags.StringLike, /*isTypeOfKind*/ true);
var hasStringConstituent = arrayOrStringType !== emptyObjectType && arrayOrStringType !== arrayType;

var reportedError = false;
if (hasStringConstituent && languageVersion < ScriptTarget.ES5) {
error(expressionForError, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
reportedError = true;
}

if (isJustString) {
return stringType;
}

if (!isArrayLikeType(arrayType)) {
if (!reportedError) {
error(expressionForError, Diagnostics.Type_0_is_not_an_array_type, typeToString(arrayType));
}
return hasStringConstituent ? stringType : unknownType;
}

var arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number) || unknownType;
if (hasStringConstituent) {
// This is just an optimization for the case where arrayOrStringType is string | string[]
if (arrayElementType.flags & TypeFlags.StringLike) {
return stringType;
}

return getUnionType([arrayElementType, stringType]);
}

return arrayElementType;
}

function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
// Grammar checking
checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ module ts {
The_left_hand_side_of_a_for_in_statement_cannot_be_a_destructuring_pattern: { code: 2491, category: DiagnosticCategory.Error, key: "The left-hand side of a 'for...in' statement cannot be a destructuring pattern." },
Cannot_redeclare_identifier_0_in_catch_clause: { code: 2492, category: DiagnosticCategory.Error, key: "Cannot redeclare identifier '{0}' in catch clause" },
Tuple_type_0_with_length_1_cannot_be_assigned_to_tuple_with_length_2: { code: 2493, category: DiagnosticCategory.Error, key: "Tuple type '{0}' with length '{1}' cannot be assigned to tuple with length '{2}'." },
Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher: { code: 2494, category: DiagnosticCategory.Error, key: "Using a string in a 'for...of' statement is only supported in ECMAScript 5 and higher." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1343,6 +1343,10 @@
"category": "Error",
"code": 2493
},
"Using a string in a 'for...of' statement is only supported in ECMAScript 5 and higher.": {
"category": "Error",
"code": 2494
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
29 changes: 29 additions & 0 deletions tests/baselines/reference/ES5For-of10.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of10.ts ===
function foo() {
>foo : () => { x: number; }

return { x: 0 };
>{ x: 0 } : { x: number; }
>x : number
}
for (foo().x of []) {
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
>[] : undefined[]

for (foo().x of [])
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
>[] : undefined[]

var p = foo().x;
>p : number
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
}
8 changes: 8 additions & 0 deletions tests/baselines/reference/ES5For-of11.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of11.ts ===
var v;
>v : any

for (v of []) { }
>v : any
>[] : undefined[]

6 changes: 3 additions & 3 deletions tests/baselines/reference/ES5For-of12.errors.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of12.ts(1,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of12.ts(1,6): error TS2461: Type 'undefined' is not an array type.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of12.ts (1 errors) ====
for ([""] of []) { }
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~~~~
!!! error TS2461: Type 'undefined' is not an array type.
9 changes: 9 additions & 0 deletions tests/baselines/reference/ES5For-of13.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of13.ts ===
for (let v of ['a', 'b', 'c']) {
>v : string
>['a', 'b', 'c'] : string[]

var x = v;
>x : string
>v : string
}
12 changes: 12 additions & 0 deletions tests/baselines/reference/ES5For-of24.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of24.ts ===
var a = [1, 2, 3];
>a : number[]
>[1, 2, 3] : number[]

for (var v of a) {
>v : number
>a : number[]

let a = 0;
>a : number
}
15 changes: 15 additions & 0 deletions tests/baselines/reference/ES5For-of25.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of25.ts ===
var a = [1, 2, 3];
>a : number[]
>[1, 2, 3] : number[]

for (var v of a) {
>v : number
>a : number[]

v;
>v : number

a;
>a : number[]
}
10 changes: 10 additions & 0 deletions tests/baselines/reference/ES5For-of26.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of26.ts(1,10): error TS2461: Type 'number' is not an array type.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of26.ts (1 errors) ====
for (var [a = 0, b = 1] of [2, 3]) {
~~~~~~~~~~~~~~
!!! error TS2461: Type 'number' is not an array type.
a;
b;
}
12 changes: 0 additions & 12 deletions tests/baselines/reference/ES5For-of26.types

This file was deleted.

11 changes: 7 additions & 4 deletions tests/baselines/reference/ES5For-of27.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts(1,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts(1,11): error TS2459: Type 'number' has no property 'x' and no string index signature.
tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts(1,21): error TS2459: Type 'number' has no property 'y' and no string index signature.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts (1 errors) ====
==== tests/cases/conformance/statements/for-ofStatements/ES5For-of27.ts (2 errors) ====
for (var {x: a = 0, y: b = 1} of [2, 3]) {
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~
!!! error TS2459: Type 'number' has no property 'x' and no string index signature.
~
!!! error TS2459: Type 'number' has no property 'y' and no string index signature.
a;
b;
}
10 changes: 10 additions & 0 deletions tests/baselines/reference/ES5For-of28.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of28.ts(1,10): error TS2461: Type 'number' is not an array type.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of28.ts (1 errors) ====
for (let [a = 0, b = 1] of [2, 3]) {
~~~~~~~~~~~~~~
!!! error TS2461: Type 'number' is not an array type.
a;
b;
}
12 changes: 0 additions & 12 deletions tests/baselines/reference/ES5For-of28.types

This file was deleted.

11 changes: 7 additions & 4 deletions tests/baselines/reference/ES5For-of29.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of29.ts(1,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of29.ts(1,13): error TS2459: Type 'number' has no property 'x' and no string index signature.
tests/cases/conformance/statements/for-ofStatements/ES5For-of29.ts(1,23): error TS2459: Type 'number' has no property 'y' and no string index signature.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of29.ts (1 errors) ====
==== tests/cases/conformance/statements/for-ofStatements/ES5For-of29.ts (2 errors) ====
for (const {x: a = 0, y: b = 1} of [2, 3]) {
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~
!!! error TS2459: Type 'number' has no property 'x' and no string index signature.
~
!!! error TS2459: Type 'number' has no property 'y' and no string index signature.
a;
b;
}
9 changes: 9 additions & 0 deletions tests/baselines/reference/ES5For-of3.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of3.ts ===
for (var v of ['a', 'b', 'c'])
>v : string
>['a', 'b', 'c'] : string[]

var x = v;
>x : string
>v : string

6 changes: 3 additions & 3 deletions tests/baselines/reference/ES5For-of30.errors.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts(3,6): error TS2461: Type 'string | number' is not an array type.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of30.ts (1 errors) ====
var a: string, b: number;
var tuple: [number, string] = [2, "3"];
for ([a = 1, b = ""] of tuple) {
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~~~~~~~~~~~~~~~
!!! error TS2461: Type 'string | number' is not an array type.
a;
b;
}
11 changes: 7 additions & 4 deletions tests/baselines/reference/ES5For-of31.errors.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of31.ts(3,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of31.ts(3,8): error TS2459: Type 'undefined' has no property 'a' and no string index signature.
tests/cases/conformance/statements/for-ofStatements/ES5For-of31.ts(3,18): error TS2459: Type 'undefined' has no property 'b' and no string index signature.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of31.ts (1 errors) ====
==== tests/cases/conformance/statements/for-ofStatements/ES5For-of31.ts (2 errors) ====
var a: string, b: number;

for ({ a: b = 1, b: a = ""} of []) {
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~
!!! error TS2459: Type 'undefined' has no property 'a' and no string index signature.
~
!!! error TS2459: Type 'undefined' has no property 'b' and no string index signature.
a;
b;
}
6 changes: 3 additions & 3 deletions tests/baselines/reference/ES5For-of8.errors.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
tests/cases/conformance/statements/for-ofStatements/ES5For-of8.ts(4,1): error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
tests/cases/conformance/statements/for-ofStatements/ES5For-of8.ts(4,6): error TS2322: Type 'string' is not assignable to type 'number'.


==== tests/cases/conformance/statements/for-ofStatements/ES5For-of8.ts (1 errors) ====
function foo() {
return { x: 0 };
}
for (foo().x of ['a', 'b', 'c']) {
~~~
!!! error TS2482: 'for...of' statements are only available when targeting ECMAScript 6 or higher.
~~~~~~~
!!! error TS2322: Type 'string' is not assignable to type 'number'.
var p = foo().x;
}
30 changes: 30 additions & 0 deletions tests/baselines/reference/ES5For-of9.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
=== tests/cases/conformance/statements/for-ofStatements/ES5For-of9.ts ===
function foo() {
>foo : () => { x: number; }

return { x: 0 };
>{ x: 0 } : { x: number; }
>x : number
}
for (foo().x of []) {
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
>[] : undefined[]

for (foo().x of []) {
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
>[] : undefined[]

var p = foo().x;
>p : number
>foo().x : number
>foo() : { x: number; }
>foo : () => { x: number; }
>x : number
}
}
Loading

0 comments on commit 61cd2a7

Please sign in to comment.