From fbce0a56de8d421d843857470a37ab10d146f86f Mon Sep 17 00:00:00 2001 From: Jason Freeman Date: Mon, 4 May 2015 14:27:33 -0700 Subject: [PATCH] Cache element types of iterable and iterator --- src/compiler/checker.ts | 104 ++++++++++++++++++++++------------------ src/compiler/types.ts | 7 +++ 2 files changed, 65 insertions(+), 46 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c637b03f19..3a57bd9fdff 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7317,7 +7317,7 @@ module ts { if (yieldExpression.asteriskToken) { // A yield* expression effectively yields everything that its operand yields - type = checkIteratedType(type, yieldExpression.expression); + type = checkElementTypeOfIterable(type, yieldExpression.expression); } if (!contains(aggregatedTypes, type)) { @@ -7989,7 +7989,7 @@ module ts { let signatureElementType = getElementTypeFromIterableIterator(getTypeFromTypeNode(func.type), /*errorNode*/ undefined) || unknownType; let expressionType = checkExpressionCached(node.expression, /*contextualMapper*/ undefined); if (node.asteriskToken) { - let expressionElementType = checkIteratedType(expressionType, node.expression); + let expressionElementType = checkElementTypeOfIterable(expressionType, node.expression); checkTypeAssignableTo(expressionElementType, signatureElementType, node.expression, /*headMessage*/ undefined); } else { @@ -9586,7 +9586,7 @@ module ts { } if (languageVersion >= ScriptTarget.ES6) { - return checkIteratedType(inputType, errorNode); + return checkElementTypeOfIterable(inputType, errorNode); } if (allowStringInput) { @@ -9607,7 +9607,7 @@ module ts { /** * When errorNode is undefined, it means we should not report any errors. */ - function checkIteratedType(iterable: Type, errorNode: Node): Type { + function checkElementTypeOfIterable(iterable: Type, errorNode: Node): Type { let elementType = getElementTypeFromIterable(iterable, errorNode); // Now even though we have extracted the iteratedType, we will have to validate that the type // passed in is actually an Iterable. @@ -9643,26 +9643,32 @@ module ts { return undefined; } - // As an optimization, if the type is instantiated directly using the globalIterableType (Iterable), - // then just grab its type argument. - if ((iterable.flags & TypeFlags.Reference) && (iterable).target === globalIterableType) { - return (iterable).typeArguments[0]; - } + let typeAsIterable = iterable; + if (!typeAsIterable.iterableElementType) { + // As an optimization, if the type is instantiated directly using the globalIterableType (Iterable), + // then just grab its type argument. + if ((iterable.flags & TypeFlags.Reference) && (iterable).target === globalIterableType) { + typeAsIterable.iterableElementType = (iterable).typeArguments[0]; + } + else { + let iteratorFunction = getTypeOfPropertyOfType(iterable, getPropertyNameForKnownSymbolName("iterator")); + if (iteratorFunction && allConstituentTypesHaveKind(iteratorFunction, TypeFlags.Any)) { + return undefined; + } - let iteratorFunction = getTypeOfPropertyOfType(iterable, getPropertyNameForKnownSymbolName("iterator")); - if (iteratorFunction && allConstituentTypesHaveKind(iteratorFunction, TypeFlags.Any)) { - return undefined; - } + let iteratorFunctionSignatures = iteratorFunction ? getSignaturesOfType(iteratorFunction, SignatureKind.Call) : emptyArray; + if (iteratorFunctionSignatures.length === 0) { + if (errorNode) { + error(errorNode, Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator); + } + return undefined; + } - let iteratorFunctionSignatures = iteratorFunction ? getSignaturesOfType(iteratorFunction, SignatureKind.Call) : emptyArray; - if (iteratorFunctionSignatures.length === 0) { - if (errorNode) { - error(errorNode, Diagnostics.Type_must_have_a_Symbol_iterator_method_that_returns_an_iterator); + typeAsIterable.iterableElementType = getElementTypeFromIterator(getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature)), errorNode); } - return undefined; } - return getElementTypeFromIterator(getUnionType(map(iteratorFunctionSignatures, getReturnTypeOfSignature)), errorNode); + return typeAsIterable.iterableElementType; } function getElementTypeFromIterator(iterator: Type, errorNode: Node): Type { @@ -9681,39 +9687,45 @@ module ts { return undefined; } - // As an optimization, if the type is instantiated directly using the globalIteratorType (Iterator), - // then just grab its type argument. - if ((iterator.flags & TypeFlags.Reference) && (iterator).target === globalIteratorType) { - return (iterator).typeArguments[0]; - } - - let iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next"); - if (iteratorNextFunction && allConstituentTypesHaveKind(iteratorNextFunction, TypeFlags.Any)) { - return undefined; - } - - let iteratorNextFunctionSignatures = iteratorNextFunction ? getSignaturesOfType(iteratorNextFunction, SignatureKind.Call) : emptyArray; - if (iteratorNextFunctionSignatures.length === 0) { - if (errorNode) { - error(errorNode, Diagnostics.An_iterator_must_have_a_next_method); + let typeAsIterator = iterator; + if (!typeAsIterator.iteratorElementType) { + // As an optimization, if the type is instantiated directly using the globalIteratorType (Iterator), + // then just grab its type argument. + if ((iterator.flags & TypeFlags.Reference) && (iterator).target === globalIteratorType) { + typeAsIterator.iteratorElementType = (iterator).typeArguments[0]; } - return undefined; - } + else { + let iteratorNextFunction = getTypeOfPropertyOfType(iterator, "next"); + if (iteratorNextFunction && allConstituentTypesHaveKind(iteratorNextFunction, TypeFlags.Any)) { + return undefined; + } - let iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature)); - if (allConstituentTypesHaveKind(iteratorNextResult, TypeFlags.Any)) { - return undefined; - } + let iteratorNextFunctionSignatures = iteratorNextFunction ? getSignaturesOfType(iteratorNextFunction, SignatureKind.Call) : emptyArray; + if (iteratorNextFunctionSignatures.length === 0) { + if (errorNode) { + error(errorNode, Diagnostics.An_iterator_must_have_a_next_method); + } + return undefined; + } - let iteratorNextValue = getTypeOfPropertyOfType(iteratorNextResult, "value"); - if (!iteratorNextValue) { - if (errorNode) { - error(errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property); + let iteratorNextResult = getUnionType(map(iteratorNextFunctionSignatures, getReturnTypeOfSignature)); + if (allConstituentTypesHaveKind(iteratorNextResult, TypeFlags.Any)) { + return undefined; + } + + let iteratorNextValue = getTypeOfPropertyOfType(iteratorNextResult, "value"); + if (!iteratorNextValue) { + if (errorNode) { + error(errorNode, Diagnostics.The_type_returned_by_the_next_method_of_an_iterator_must_have_a_value_property); + } + return undefined; + } + + typeAsIterator.iteratorElementType = iteratorNextValue; } - return undefined; } - return iteratorNextValue; + return typeAsIterator.iteratorElementType; } function getElementTypeFromIterableIterator(iterableIterator: Type, errorNode: Node): Type { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a11d97c45fb..2554bd45b4c 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1543,6 +1543,13 @@ module ts { numberIndexType: Type; // Numeric index type } + /* @internal */ + // Just a place to cache element types of iterables and iterators + export interface IterableOrIteratorType extends ObjectType, UnionType { + iterableElementType?: Type; + iteratorElementType?: Type; + } + // Type parameters (TypeFlags.TypeParameter) export interface TypeParameter extends Type { constraint: Type; // Constraint