In ES6, an Iterable should contextually type an array literal

This commit is contained in:
Jason Freeman 2015-02-25 18:15:48 -08:00
parent c00a264bb7
commit f3543b718f
10 changed files with 115 additions and 15 deletions

View File

@ -5461,7 +5461,9 @@ module ts {
var type = getContextualType(arrayLiteral);
if (type) {
var index = indexOf(arrayLiteral.elements, node);
return getTypeOfPropertyOfContextualType(type, "" + index) || getIndexTypeOfContextualType(type, IndexKind.Number);
return getTypeOfPropertyOfContextualType(type, "" + index)
|| getIndexTypeOfContextualType(type, IndexKind.Number)
|| (languageVersion >= ScriptTarget.ES6 ? getIteratedType(type, /*expressionForError*/ undefined) : undefined);
}
return undefined;
}
@ -8770,7 +8772,14 @@ module ts {
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);
checkTypeAssignableTo(iteratedType, leftType, varExpr, /*headMessage*/ undefined);
// 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);
}
}
checkSourceElement(node.statement);
@ -8828,15 +8837,22 @@ module ts {
return anyType;
}
return getIteratedType(getTypeOfExpression(forOfStatement.expression), forOfStatement.expression);
// iteratedType will be undefined if the for-of expression type 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.
return getIteratedType(getTypeOfExpression(forOfStatement.expression), forOfStatement.expression) || anyType;
}
/**
* When expressionForError is undefined, it means we should not report any errors.
*/
function getIteratedType(iterable: Type, expressionForError: Expression): Type {
Debug.assert(languageVersion >= ScriptTarget.ES6);
var iteratedType = getIteratedTypeSubroutine(iterable, expressionForError);
// Now even though we have extracted the iteratedType, we will have to validate that the type
// passed in is actually an Iterable.
if (iteratedType !== unknownType) {
if (expressionForError && iteratedType) {
var completeIterableType = globalIterableType !== emptyObjectType ? createTypeReference(<GenericType>globalIterableType, [iteratedType]) : emptyObjectType;
checkTypeAssignableTo(iterable, completeIterableType, expressionForError);
}
@ -8845,7 +8861,7 @@ module ts {
function getIteratedTypeSubroutine(iterable: Type, expressionForError: Expression) {
if (allConstituentTypesHaveKind(iterable, TypeFlags.Any)) {
return iterable; // any or unknown
return undefined;
}
// We want to treat type as an iterable, and get the type it is an iterable of. The iterable
@ -8867,40 +8883,46 @@ module ts {
// of signatures, we union the return types of all the signatures.
var iteratorFunction = getTypeOfPropertyOfType(iterable, getPropertyNameForKnownSymbolName("iterator"));
if (iteratorFunction && allConstituentTypesHaveKind(iteratorFunction, TypeFlags.Any)) {
return iteratorFunction; // any or unknown
return undefined;
}
var iteratorFunctionSignatures = iteratorFunction ? getSignaturesOfType(iteratorFunction, SignatureKind.Call) : emptyArray;
if (iteratorFunctionSignatures.length === 0) {
error(expressionForError, Diagnostics.The_right_hand_side_of_a_for_of_statement_must_have_a_Symbol_iterator_method_that_returns_an_iterator);
return unknownType;
if (expressionForError) {
error(expressionForError, Diagnostics.The_right_hand_side_of_a_for_of_statement_must_have_a_Symbol_iterator_method_that_returns_an_iterator);
}
return undefined;
}
var iterator = getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iterator, TypeFlags.Any)) {
return iterator; // any or unknown
return undefined;
}
var iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next");
if (iteratorNextFunction && allConstituentTypesHaveKind(iteratorNextFunction, TypeFlags.Any)) {
return iteratorNextFunction; // any or unknown
return undefined;
}
var iteratorNextFunctionSignatures = iteratorNextFunction ? getSignaturesOfType(iteratorNextFunction, SignatureKind.Call) : emptyArray;
if (iteratorNextFunctionSignatures.length === 0) {
error(expressionForError, Diagnostics.The_iterator_returned_by_the_right_hand_side_of_a_for_of_statement_must_have_a_next_method);
return unknownType;
if (expressionForError) {
error(expressionForError, Diagnostics.The_iterator_returned_by_the_right_hand_side_of_a_for_of_statement_must_have_a_next_method);
}
return undefined;
}
var iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iteratorNextResult, TypeFlags.Any)) {
return iteratorNextResult; // any or unknown
return undefined;
}
var iteratorNextValue = getTypeOfPropertyOfType(iteratorNextResult, "value");
if (!iteratorNextValue) {
error(expressionForError, Diagnostics.The_object_returned_by_the_next_method_of_the_iterator_must_have_a_value_property);
return unknownType;
if (expressionForError) {
error(expressionForError, Diagnostics.The_object_returned_by_the_next_method_of_the_iterator_must_have_a_value_property);
}
return undefined;
}
return iteratorNextValue;

View File

@ -0,0 +1,11 @@
//// [for-of36.ts]
var tuple: [string, boolean] = ["", true];
for (var v of tuple) {
v;
}
//// [for-of36.js]
var tuple = ["", true];
for (var v of tuple) {
v;
}

View File

@ -0,0 +1,12 @@
=== tests/cases/conformance/es6/for-ofStatements/for-of36.ts ===
var tuple: [string, boolean] = ["", true];
>tuple : [string, boolean]
>["", true] : [string, boolean]
for (var v of tuple) {
>v : string | boolean
>tuple : [string, boolean]
v;
>v : string | boolean
}

View File

@ -0,0 +1,11 @@
//// [for-of37.ts]
var map = new Map([["", true]]);
for (var v of map) {
v;
}
//// [for-of37.js]
var map = new Map([["", true]]);
for (var v of map) {
v;
}

View File

@ -0,0 +1,15 @@
=== tests/cases/conformance/es6/for-ofStatements/for-of37.ts ===
var map = new Map([["", true]]);
>map : Map<string, boolean>
>new Map([["", true]]) : Map<string, boolean>
>Map : MapConstructor
>[["", true]] : [string, boolean][]
>["", true] : [string, boolean]
for (var v of map) {
>v : [string, boolean]
>map : Map<string, boolean>
v;
>v : [string, boolean]
}

View File

@ -0,0 +1,5 @@
//// [iterableContextualTyping1.ts]
var iter: Iterable<(x: string) => number> = [s => s.length];
//// [iterableContextualTyping1.js]
var iter = [s => s.length];

View File

@ -0,0 +1,12 @@
=== tests/cases/conformance/expressions/contextualTyping/iterableContextualTyping1.ts ===
var iter: Iterable<(x: string) => number> = [s => s.length];
>iter : Iterable<(x: string) => number>
>Iterable : Iterable<T>
>x : string
>[s => s.length] : ((s: string) => number)[]
>s => s.length : (s: string) => number
>s : string
>s.length : number
>s : string
>length : number

View File

@ -0,0 +1,5 @@
//@target: ES6
var tuple: [string, boolean] = ["", true];
for (var v of tuple) {
v;
}

View File

@ -0,0 +1,5 @@
//@target: ES6
var map = new Map([["", true]]);
for (var v of map) {
v;
}

View File

@ -0,0 +1,2 @@
//@target: ES6
var iter: Iterable<(x: string) => number> = [s => s.length];