Check for assignability to the relevant iteratable type

This commit is contained in:
Jason Freeman 2015-02-24 16:09:58 -08:00
parent 732637dd54
commit 0049b21d6c
10 changed files with 272 additions and 65 deletions

View file

@ -97,6 +97,7 @@ module ts {
var globalRegExpType: ObjectType;
var globalTemplateStringsArrayType: ObjectType;
var globalESSymbolType: ObjectType;
var globalIterableType: ObjectType;
var anyArrayType: Type;
@ -3156,8 +3157,8 @@ module ts {
return resolveName(undefined, name, meaning, diagnostic, name);
}
function getGlobalType(name: string): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), 0);
function getGlobalType(name: string, arity = 0): ObjectType {
return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity);
}
function getGlobalESSymbolConstructorSymbol() {
@ -8844,70 +8845,79 @@ module ts {
}
function getIteratedType(iterable: Type, expressionForError: Expression): Type {
Debug.assert(languageVersion >= ScriptTarget.ES6);
if (allConstituentTypesHaveKind(iterable, TypeFlags.Any)) {
return iterable; // any or unknown
}
// We want to treat type as an iterable, and get the type it is an iterable of. The iterable
// must have the following structure (annotated with the names of the variables below):
//
// { // iterable
// [Symbol.iterator]: { // iteratorFunction
// (): { // iterator
// next: { // iteratorNextFunction
// (): { // iteratorNextResult
// value: T // iteratorNextValue
// }
// }
// }
// }
// }
//
// T is the type we are after. At every level that involves analyzing return types
// 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
}
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;
}
var iterator = getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iterator, TypeFlags.Any)) {
return iterator; // any or unknown
}
var iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next");
if (iteratorNextFunction && allConstituentTypesHaveKind(iteratorNextFunction, TypeFlags.Any)) {
return iteratorNextFunction; // any or unknown
}
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;
}
var iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iteratorNextResult, TypeFlags.Any)) {
return iteratorNextResult; // any or unknown
}
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;
}
return iteratorNextValue;
// TODO
// Now even though we have extracted the iteratorNextValue, we will have to validate that the type
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) {
var completeIterableType = globalIterableType !== emptyObjectType ? createTypeReference(<GenericType>globalIterableType, [iteratedType]) : emptyObjectType;
checkTypeAssignableTo(iterable, completeIterableType, expressionForError);
}
return iteratedType;
function getIteratedTypeSubroutine(iterable: Type, expressionForError: Expression) {
Debug.assert(languageVersion >= ScriptTarget.ES6);
if (allConstituentTypesHaveKind(iterable, TypeFlags.Any)) {
return iterable; // any or unknown
}
// We want to treat type as an iterable, and get the type it is an iterable of. The iterable
// must have the following structure (annotated with the names of the variables below):
//
// { // iterable
// [Symbol.iterator]: { // iteratorFunction
// (): { // iterator
// next: { // iteratorNextFunction
// (): { // iteratorNextResult
// value: T // iteratorNextValue
// }
// }
// }
// }
// }
//
// T is the type we are after. At every level that involves analyzing return types
// 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
}
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;
}
var iterator = getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iterator, TypeFlags.Any)) {
return iterator; // any or unknown
}
var iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next");
if (iteratorNextFunction && allConstituentTypesHaveKind(iteratorNextFunction, TypeFlags.Any)) {
return iteratorNextFunction; // any or unknown
}
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;
}
var iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature));
if (allConstituentTypesHaveKind(iteratorNextResult, TypeFlags.Any)) {
return iteratorNextResult; // any or unknown
}
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;
}
return iteratorNextValue;
}
}
function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
@ -10856,6 +10866,7 @@ module ts {
globalTemplateStringsArrayType = getGlobalType("TemplateStringsArray");
globalESSymbolType = getGlobalType("Symbol");
globalESSymbolConstructorSymbol = getGlobalValueSymbol("Symbol");
globalIterableType = getGlobalType("Iterable", 1);
}
else {
globalTemplateStringsArrayType = unknownType;

View file

@ -0,0 +1,14 @@
tests/cases/conformance/es6/for-ofStatements/for-of29.ts(5,15): error TS2322: Type '{ [Symbol.iterator]?(): Iterator<string>; }' is not assignable to type 'Iterable<string>'.
Property '[Symbol.iterator]' is optional in type '{ [Symbol.iterator]?(): Iterator<string>; }' but required in type 'Iterable<string>'.
==== tests/cases/conformance/es6/for-ofStatements/for-of29.ts (1 errors) ====
var iterableWithOptionalIterator: {
[Symbol.iterator]?(): Iterator<string>
};
for (var v of iterableWithOptionalIterator) { }
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type '{ [Symbol.iterator]?(): Iterator<string>; }' is not assignable to type 'Iterable<string>'.
!!! error TS2322: Property '[Symbol.iterator]' is optional in type '{ [Symbol.iterator]?(): Iterator<string>; }' but required in type 'Iterable<string>'.

View file

@ -0,0 +1,11 @@
//// [for-of29.ts]
var iterableWithOptionalIterator: {
[Symbol.iterator]?(): Iterator<string>
};
for (var v of iterableWithOptionalIterator) { }
//// [for-of29.js]
var iterableWithOptionalIterator;
for (var v of iterableWithOptionalIterator) { }

View file

@ -0,0 +1,32 @@
tests/cases/conformance/es6/for-ofStatements/for-of30.ts(1,15): error TS2322: Type 'StringIterator' is not assignable to type 'Iterable<string>'.
Types of property '[Symbol.iterator]' are incompatible.
Type '() => StringIterator' is not assignable to type '() => Iterator<string>'.
Type 'StringIterator' is not assignable to type 'Iterator<string>'.
Types of property 'return' are incompatible.
Type 'number' is not assignable to type '(value?: any) => IteratorResult<string>'.
==== tests/cases/conformance/es6/for-ofStatements/for-of30.ts (1 errors) ====
for (var v of new StringIterator) { }
~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'StringIterator' is not assignable to type 'Iterable<string>'.
!!! error TS2322: Types of property '[Symbol.iterator]' are incompatible.
!!! error TS2322: Type '() => StringIterator' is not assignable to type '() => Iterator<string>'.
!!! error TS2322: Type 'StringIterator' is not assignable to type 'Iterator<string>'.
!!! error TS2322: Types of property 'return' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type '(value?: any) => IteratorResult<string>'.
class StringIterator {
next() {
return {
done: false,
value: ""
}
}
return = 0;
[Symbol.iterator]() {
return this;
}
}

View file

@ -0,0 +1,35 @@
//// [for-of30.ts]
for (var v of new StringIterator) { }
class StringIterator {
next() {
return {
done: false,
value: ""
}
}
return = 0;
[Symbol.iterator]() {
return this;
}
}
//// [for-of30.js]
for (var v of new StringIterator) { }
var StringIterator = (function () {
function StringIterator() {
this.return = 0;
}
StringIterator.prototype.next = function () {
return {
done: false,
value: ""
};
};
StringIterator.prototype[Symbol.iterator] = function () {
return this;
};
return StringIterator;
})();

View file

@ -0,0 +1,34 @@
tests/cases/conformance/es6/for-ofStatements/for-of31.ts(1,15): error TS2322: Type 'StringIterator' is not assignable to type 'Iterable<string>'.
Types of property '[Symbol.iterator]' are incompatible.
Type '() => StringIterator' is not assignable to type '() => Iterator<string>'.
Type 'StringIterator' is not assignable to type 'Iterator<string>'.
Types of property 'next' are incompatible.
Type '() => { value: string; }' is not assignable to type '() => IteratorResult<string>'.
Type '{ value: string; }' is not assignable to type 'IteratorResult<string>'.
Property 'done' is missing in type '{ value: string; }'.
==== tests/cases/conformance/es6/for-ofStatements/for-of31.ts (1 errors) ====
for (var v of new StringIterator) { }
~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type 'StringIterator' is not assignable to type 'Iterable<string>'.
!!! error TS2322: Types of property '[Symbol.iterator]' are incompatible.
!!! error TS2322: Type '() => StringIterator' is not assignable to type '() => Iterator<string>'.
!!! error TS2322: Type 'StringIterator' is not assignable to type 'Iterator<string>'.
!!! error TS2322: Types of property 'next' are incompatible.
!!! error TS2322: Type '() => { value: string; }' is not assignable to type '() => IteratorResult<string>'.
!!! error TS2322: Type '{ value: string; }' is not assignable to type 'IteratorResult<string>'.
!!! error TS2322: Property 'done' is missing in type '{ value: string; }'.
class StringIterator {
next() {
return {
// no done property
value: ""
}
}
[Symbol.iterator]() {
return this;
}
}

View file

@ -0,0 +1,32 @@
//// [for-of31.ts]
for (var v of new StringIterator) { }
class StringIterator {
next() {
return {
// no done property
value: ""
}
}
[Symbol.iterator]() {
return this;
}
}
//// [for-of31.js]
for (var v of new StringIterator) { }
var StringIterator = (function () {
function StringIterator() {
}
StringIterator.prototype.next = function () {
return {
// no done property
value: ""
};
};
StringIterator.prototype[Symbol.iterator] = function () {
return this;
};
return StringIterator;
})();

View file

@ -0,0 +1,6 @@
//@target: ES6
var iterableWithOptionalIterator: {
[Symbol.iterator]?(): Iterator<string>
};
for (var v of iterableWithOptionalIterator) { }

View file

@ -0,0 +1,17 @@
//@target: ES6
for (var v of new StringIterator) { }
class StringIterator {
next() {
return {
done: false,
value: ""
}
}
return = 0;
[Symbol.iterator]() {
return this;
}
}

View file

@ -0,0 +1,15 @@
//@target: ES6
for (var v of new StringIterator) { }
class StringIterator {
next() {
return {
// no done property
value: ""
}
}
[Symbol.iterator]() {
return this;
}
}