diff --git a/src/com/google/javascript/jscomp/Es6RewriteGenerators.java b/src/com/google/javascript/jscomp/Es6RewriteGenerators.java index 34e8c14cb21..7098beab471 100644 --- a/src/com/google/javascript/jscomp/Es6RewriteGenerators.java +++ b/src/com/google/javascript/jscomp/Es6RewriteGenerators.java @@ -266,7 +266,7 @@ private class SingleGeneratorFunctionTranspiler { if (genFunc.getJSType() != null && genFunc.getJSType().isFunctionType()) { FunctionType fnType = genFunc.getJSType().toMaybeFunctionType(); this.originalGenReturnType = fnType.getReturnType(); - yieldType = TypeCheck.getTemplateTypeOfGenerator(registry, originalGenReturnType); + yieldType = JsIterables.getElementType(originalGenReturnType, registry); } JSType globalContextType = registry.getGlobalType("$jscomp.generator.Context"); diff --git a/src/com/google/javascript/jscomp/JsIterables.java b/src/com/google/javascript/jscomp/JsIterables.java new file mode 100644 index 00000000000..e3a87aac9ce --- /dev/null +++ b/src/com/google/javascript/jscomp/JsIterables.java @@ -0,0 +1,55 @@ +/* + * Copyright 2014 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.google.javascript.jscomp; + +import static com.google.javascript.rhino.jstype.JSTypeNative.UNKNOWN_TYPE; + +import com.google.javascript.rhino.jstype.JSType; +import com.google.javascript.rhino.jstype.JSTypeRegistry; +import com.google.javascript.rhino.jstype.TemplateTypeMap; + +/** + * Models type transformations of JavaScript `Iterable` and `Iterator` types. + * + *

These tranformations can be especially helpful when working with generator functions. + * `Generator` is a subtype of `Iterable`. + */ +final class JsIterables { + + /** + * Returns the given `Iterable`s element type. + * + *

If the given type is not an `Iterator` or `Iterable`, returns the unknown type. + */ + static final JSType getElementType(JSType iterableOrIterator, JSTypeRegistry typeRegistry) { + TemplateTypeMap templateTypeMap = + iterableOrIterator + // Remember that `string` will box to a `Iterable`. + .autobox() + .getTemplateTypeMap(); + + if (templateTypeMap.hasTemplateKey(typeRegistry.getIterableTemplate())) { + // `Iterable` or `Generator` + return templateTypeMap.getResolvedTemplateType(typeRegistry.getIterableTemplate()); + } else if (templateTypeMap.hasTemplateKey(typeRegistry.getIteratorTemplate())) { + // `Iterator` + return templateTypeMap.getResolvedTemplateType(typeRegistry.getIteratorTemplate()); + } + return typeRegistry.getNativeType(UNKNOWN_TYPE); + } + + private JsIterables() {} +} diff --git a/src/com/google/javascript/jscomp/TypeCheck.java b/src/com/google/javascript/jscomp/TypeCheck.java index 782a736683d..fc92c7516f5 100644 --- a/src/com/google/javascript/jscomp/TypeCheck.java +++ b/src/com/google/javascript/jscomp/TypeCheck.java @@ -2552,7 +2552,7 @@ private void visitReturn(NodeTraversal t, Node n) { } else if (enclosingFunction.isGeneratorFunction()) { // Unwrap the template variable from a generator function's declared return type. // e.g. if returnType is "Generator", make it just "string". - returnType = getTemplateTypeOfGenerator(returnType); + returnType = JsIterables.getElementType(returnType, typeRegistry); } else if (enclosingFunction.isAsyncFunction()) { // e.g. `!Promise` => `string|!IThenable` // We transform the expected return type rather than the actual return type so that the @@ -2592,7 +2592,7 @@ private void visitYield(NodeTraversal t, Node n) { if (jsType.isFunctionType()) { FunctionType functionType = jsType.toMaybeFunctionType(); JSType returnType = functionType.getReturnType(); - declaredYieldType = getTemplateTypeOfGenerator(returnType); + declaredYieldType = JsIterables.getElementType(returnType, typeRegistry); } // fetching the yielded value's type @@ -2624,32 +2624,6 @@ private void visitYield(NodeTraversal t, Node n) { "Yielded type does not match declared return type."); } - private JSType getTemplateTypeOfGenerator(JSType generator) { - return getTemplateTypeOfGenerator(typeRegistry, generator); - } - - /** - * Returns the given type's resolved template type corresponding to the corresponding to the - * Generator, Iterable or Iterator template key if possible. - * - *

If the given type is not an Iterator or Iterable, returns the unknown type.. - */ - static JSType getTemplateTypeOfGenerator(JSTypeRegistry typeRegistry, JSType generator) { - TemplateTypeMap templateTypeMap = generator.autobox().getTemplateTypeMap(); - if (templateTypeMap.hasTemplateKey(typeRegistry.getIterableTemplate())) { - // Generator JSDoc says - // @return {!Iterable} - // or - // @return {!Generator} - return templateTypeMap.getResolvedTemplateType(typeRegistry.getIterableTemplate()); - } else if (templateTypeMap.hasTemplateKey(typeRegistry.getIteratorTemplate())) { - // Generator JSDoc says - // @return {!Iterator} - return templateTypeMap.getResolvedTemplateType(typeRegistry.getIteratorTemplate()); - } - return typeRegistry.getNativeType(UNKNOWN_TYPE); - } - private void visitTaggedTemplateLit(NodeTraversal t, Node n) { Node tag = n.getFirstChild(); JSType tagType = tag.getJSType().restrictByNotNullOrUndefined();