Java Code Examples for com.google.javascript.rhino.jstype.JSType#isTemplateType()

The following examples show how to use com.google.javascript.rhino.jstype.JSType#isTemplateType() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: DeclarationGenerator.java    From clutz with MIT License 6 votes vote down vote up
private void visitFunctionDeclaration(FunctionType ftype, List<String> skipTemplateParams) {
  visitFunctionParameters(ftype, true, skipTemplateParams);
  JSType type = ftype.getReturnType();
  final JSType typeOfThis = ftype.getTypeOfThis();

  if (type == null) return;
  emit(":");
  // Closure conflates 'undefined' and 'void', and in general visitType always emits `undefined`
  // for that type.
  // In idiomatic TypeScript, `void` is used for function return types, and the "void",
  // "undefined" types are not the same.
  if (type.isVoidType()) {
    emit("void");
  } else if (typeOfThis != null && typeOfThis.isTemplateType() && typeOfThis.equals(type)) {
    // Special case: prefer polymorphic `this` type to templatized `this` param
    emit("this");
  } else {
    visitType(type);
  }
}
 
Example 2
Source File: DeclarationGenerator.java    From clutz with MIT License 6 votes vote down vote up
/**
 * Special handling for simple typing returning polymorphic this type in TypeScript. Prefer
 * `func(): this` instead of `func&lt;T&gt;(this: T): T` when any params are not templatized.
 */
private boolean shouldEmitThisParam(FunctionType ftype) {
  final JSType typeOfThis = ftype.getTypeOfThis();
  if (typeOfThis == null
      || !typeOfThis.isTemplateType()
      || !typeOfThis.equals(ftype.getReturnType())) {
    return true;
  }

  for (Parameter parameter : ftype.getParameters()) {
    JSType paramType = parameter.getJSType();
    if (!paramType.isTemplatizedType()) {
      continue;
    }

    final TemplateTypeMap templateTypeMap = paramType.getTemplateTypeMap();
    for (TemplateType key : templateTypeMap.getTemplateKeys()) {
      if (templateTypeMap.getResolvedTemplateType(key).equals(typeOfThis)) {
        return true;
      }
    }
  }

  return false;
}
 
Example 3
Source File: DeclarationGenerator.java    From clutz with MIT License 6 votes vote down vote up
/**
 * Emit a this parameter like `func(this: Foo)` in a function parameters.
 *
 * <p>TODO: emit for non-templatized this like `function(this: HTMLElement)`
 */
private void emitThisParameter(FunctionType ftype, Iterator<Parameter> parameters) {
  final JSType typeOfThis = ftype.getTypeOfThis();
  // Don't emit for a constructor like `function(new: T)`.
  // A `this` parameter in a constructor is not allowed in TypeScript.
  if (typeOfThis == null || ftype.isConstructor()) {
    return;
  }
  final JSDocInfo jsDocInfo = ftype.getJSDocInfo();
  // Emit for templatized this param like `function(this: T)` or JSDoc `@this` type.
  if (!typeOfThis.isTemplateType() && (jsDocInfo == null || jsDocInfo.getThisType() == null)) {
    return;
  }
  emitNoSpace("this :");
  visitType(typeOfThis);
  if (parameters.hasNext()) {
    emit(", ");
  }
}
 
Example 4
Source File: DeclarationGenerator.java    From clutz with MIT License 5 votes vote down vote up
private boolean allParametersUnknown(FunctionType ftype) {
  for (Parameter param : ftype.getParameters()) {
    JSType type = param.getJSType();
    // Note: template types (e.g. the T in Array<T>) return true for isUnknownType,
    // so we check that first.
    if (type.isTemplateType() || !type.isUnknownType()) return false;
  }
  return true;
}
 
Example 5
Source File: Closure_25_TypeInference_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * For functions with function(this: T, ...) and T as parameters, type
 * inference will set the type of this on a function literal argument to the
 * the actual type of T.
 */
private void updateTypeOfThisOnClosure(Node n, FunctionType fnType) {
  // TODO(user): Make the template logic more general.

  if (fnType.getTemplateTypeName() == null) {
    return;
  }

  int i = 0;
  int childCount = n.getChildCount();
  // Find the parameter whose type is the template type.
  for (Node iParameter : fnType.getParameters()) {
    JSType iParameterType =
        getJSType(iParameter).restrictByNotNullOrUndefined();
    if (iParameterType.isTemplateType()) {
      // Find the actual type of this argument.
      ObjectType iArgumentType = null;
      if (i + 1 < childCount) {
        Node iArgument = n.getChildAtIndex(i + 1);
        iArgumentType = getJSType(iArgument)
            .restrictByNotNullOrUndefined()
            .collapseUnion()
            .toObjectType();
        if (iArgumentType == null) {
          compiler.report(
              JSError.make(NodeUtil.getSourceName(iArgument), iArgument,
                  TEMPLATE_TYPE_NOT_OBJECT_TYPE,
                  getJSType(iArgument).toString()));
          return;
        }
      }

      // Find the parameter whose type is function(this: T, ...)
      boolean foundTemplateTypeOfThisParameter = false;
      int j = 0;
      for (Node jParameter : fnType.getParameters()) {
        JSType jParameterType =
            getJSType(jParameter).restrictByNotNullOrUndefined();
        if (jParameterType.isFunctionType()) {
          FunctionType jParameterFnType = jParameterType.toMaybeFunctionType();
          if (jParameterFnType.getTypeOfThis().equals(iParameterType)) {
            foundTemplateTypeOfThisParameter = true;
            // Find the actual type of the this argument.
            if (j + 1 >= childCount) {
              // TypeCheck#visitParameterList will warn so we bail.
              return;
            }
            Node jArgument = n.getChildAtIndex(j + 1);
            JSType jArgumentType = getJSType(jArgument);
            if (jArgument.isFunction() &&
                jArgumentType.isFunctionType()) {
              if (iArgumentType != null &&
                  // null and undefined get filtered out above.
                  !iArgumentType.isNoType()) {
                // If it's an function expression, update the type of this
                // using the actual type of T.
                FunctionType jArgumentFnType = jArgumentType.toMaybeFunctionType();
                if (jArgumentFnType.getTypeOfThis().isUnknownType()) {
                  // The new type will be picked up when we traverse the inner
                  // function.
                  jArgument.setJSType(
                      registry.createFunctionTypeWithNewThisType(
                          jArgumentFnType, iArgumentType));
                }
              } else {
                // Warn if the anonymous function literal references this.
                if (NodeUtil.referencesThis(
                        NodeUtil.getFunctionBody(jArgument))) {
                  compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
                      FUNCTION_LITERAL_UNDEFINED_THIS));
                }
              }
            }
            // TODO(user): Add code to TypeCheck to check that the
            // types of the arguments match.
          }
        }
        j++;
      }

      if (!foundTemplateTypeOfThisParameter) {
        compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
            TEMPLATE_TYPE_OF_THIS_EXPECTED));
        return;
      }
    }
    i++;
  }
}
 
Example 6
Source File: Closure_25_TypeInference_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * For functions with function(this: T, ...) and T as parameters, type
 * inference will set the type of this on a function literal argument to the
 * the actual type of T.
 */
private void updateTypeOfThisOnClosure(Node n, FunctionType fnType) {
  // TODO(user): Make the template logic more general.

  if (fnType.getTemplateTypeName() == null) {
    return;
  }

  int i = 0;
  int childCount = n.getChildCount();
  // Find the parameter whose type is the template type.
  for (Node iParameter : fnType.getParameters()) {
    JSType iParameterType =
        getJSType(iParameter).restrictByNotNullOrUndefined();
    if (iParameterType.isTemplateType()) {
      // Find the actual type of this argument.
      ObjectType iArgumentType = null;
      if (i + 1 < childCount) {
        Node iArgument = n.getChildAtIndex(i + 1);
        iArgumentType = getJSType(iArgument)
            .restrictByNotNullOrUndefined()
            .collapseUnion()
            .toObjectType();
        if (iArgumentType == null) {
          compiler.report(
              JSError.make(NodeUtil.getSourceName(iArgument), iArgument,
                  TEMPLATE_TYPE_NOT_OBJECT_TYPE,
                  getJSType(iArgument).toString()));
          return;
        }
      }

      // Find the parameter whose type is function(this: T, ...)
      boolean foundTemplateTypeOfThisParameter = false;
      int j = 0;
      for (Node jParameter : fnType.getParameters()) {
        JSType jParameterType =
            getJSType(jParameter).restrictByNotNullOrUndefined();
        if (jParameterType.isFunctionType()) {
          FunctionType jParameterFnType = jParameterType.toMaybeFunctionType();
          if (jParameterFnType.getTypeOfThis().equals(iParameterType)) {
            foundTemplateTypeOfThisParameter = true;
            // Find the actual type of the this argument.
            if (j + 1 >= childCount) {
              // TypeCheck#visitParameterList will warn so we bail.
              return;
            }
            Node jArgument = n.getChildAtIndex(j + 1);
            JSType jArgumentType = getJSType(jArgument);
            if (jArgument.isFunction() &&
                jArgumentType.isFunctionType()) {
              if (iArgumentType != null &&
                  // null and undefined get filtered out above.
                  !iArgumentType.isNoType()) {
                // If it's an function expression, update the type of this
                // using the actual type of T.
                FunctionType jArgumentFnType = jArgumentType.toMaybeFunctionType();
                if (jArgumentFnType.getTypeOfThis().isUnknownType()) {
                  // The new type will be picked up when we traverse the inner
                  // function.
                  jArgument.setJSType(
                      registry.createFunctionTypeWithNewThisType(
                          jArgumentFnType, iArgumentType));
                }
              } else {
                // Warn if the anonymous function literal references this.
                if (NodeUtil.referencesThis(
                        NodeUtil.getFunctionBody(jArgument))) {
                  compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
                      FUNCTION_LITERAL_UNDEFINED_THIS));
                }
              }
            }
            // TODO(user): Add code to TypeCheck to check that the
            // types of the arguments match.
          }
        }
        j++;
      }

      if (!foundTemplateTypeOfThisParameter) {
        compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
            TEMPLATE_TYPE_OF_THIS_EXPECTED));
        return;
      }
    }
    i++;
  }
}
 
Example 7
Source File: Closure_35_TypeInference_s.java    From coming with MIT License 4 votes vote down vote up
/**
 * For functions with function(this: T, ...) and T as parameters, type
 * inference will set the type of this on a function literal argument to the
 * the actual type of T.
 */
private void updateTypeOfThisOnClosure(Node n, FunctionType fnType) {
  // TODO(user): Make the template logic more general.

  if (fnType.getTemplateTypeName() == null) {
    return;
  }

  int i = 0;
  int childCount = n.getChildCount();
  // Find the parameter whose type is the template type.
  for (Node iParameter : fnType.getParameters()) {
    JSType iParameterType =
        getJSType(iParameter).restrictByNotNullOrUndefined();
    if (iParameterType.isTemplateType()) {
      // Find the actual type of this argument.
      ObjectType iArgumentType = null;
      if (i + 1 < childCount) {
        Node iArgument = n.getChildAtIndex(i + 1);
        iArgumentType = getJSType(iArgument)
            .restrictByNotNullOrUndefined()
            .collapseUnion()
            .toObjectType();
        if (iArgumentType == null) {
          compiler.report(
              JSError.make(NodeUtil.getSourceName(iArgument), iArgument,
                  TEMPLATE_TYPE_NOT_OBJECT_TYPE,
                  getJSType(iArgument).toString()));
          return;
        }
      }

      // Find the parameter whose type is function(this: T, ...)
      boolean foundTemplateTypeOfThisParameter = false;
      int j = 0;
      for (Node jParameter : fnType.getParameters()) {
        JSType jParameterType =
            getJSType(jParameter).restrictByNotNullOrUndefined();
        if (jParameterType.isFunctionType()) {
          FunctionType jParameterFnType = jParameterType.toMaybeFunctionType();
          if (jParameterFnType.getTypeOfThis().equals(iParameterType)) {
            foundTemplateTypeOfThisParameter = true;
            // Find the actual type of the this argument.
            if (j + 1 >= childCount) {
              // TypeCheck#visitParameterList will warn so we bail.
              return;
            }
            Node jArgument = n.getChildAtIndex(j + 1);
            JSType jArgumentType = getJSType(jArgument);
            if (jArgument.isFunction() &&
                jArgumentType.isFunctionType()) {
              if (iArgumentType != null &&
                  // null and undefined get filtered out above.
                  !iArgumentType.isNoType()) {
                // If it's an function expression, update the type of this
                // using the actual type of T.
                FunctionType jArgumentFnType = jArgumentType.toMaybeFunctionType();
                if (jArgumentFnType.getTypeOfThis().isUnknownType()) {
                  // The new type will be picked up when we traverse the inner
                  // function.
                  jArgument.setJSType(
                      registry.createFunctionTypeWithNewThisType(
                          jArgumentFnType, iArgumentType));
                }
              } else {
                // Warn if the anonymous function literal references this.
                if (NodeUtil.referencesThis(
                        NodeUtil.getFunctionBody(jArgument))) {
                  compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
                      FUNCTION_LITERAL_UNDEFINED_THIS));
                }
              }
            }
            // TODO(user): Add code to TypeCheck to check that the
            // types of the arguments match.
          }
        }
        j++;
      }

      if (!foundTemplateTypeOfThisParameter) {
        compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
            TEMPLATE_TYPE_OF_THIS_EXPECTED));
        return;
      }
    }
    i++;
  }
}
 
Example 8
Source File: Closure_35_TypeInference_t.java    From coming with MIT License 4 votes vote down vote up
/**
 * For functions with function(this: T, ...) and T as parameters, type
 * inference will set the type of this on a function literal argument to the
 * the actual type of T.
 */
private void updateTypeOfThisOnClosure(Node n, FunctionType fnType) {
  // TODO(user): Make the template logic more general.

  if (fnType.getTemplateTypeName() == null) {
    return;
  }

  int i = 0;
  int childCount = n.getChildCount();
  // Find the parameter whose type is the template type.
  for (Node iParameter : fnType.getParameters()) {
    JSType iParameterType =
        getJSType(iParameter).restrictByNotNullOrUndefined();
    if (iParameterType.isTemplateType()) {
      // Find the actual type of this argument.
      ObjectType iArgumentType = null;
      if (i + 1 < childCount) {
        Node iArgument = n.getChildAtIndex(i + 1);
        iArgumentType = getJSType(iArgument)
            .restrictByNotNullOrUndefined()
            .collapseUnion()
            .toObjectType();
        if (iArgumentType == null) {
          compiler.report(
              JSError.make(NodeUtil.getSourceName(iArgument), iArgument,
                  TEMPLATE_TYPE_NOT_OBJECT_TYPE,
                  getJSType(iArgument).toString()));
          return;
        }
      }

      // Find the parameter whose type is function(this: T, ...)
      boolean foundTemplateTypeOfThisParameter = false;
      int j = 0;
      for (Node jParameter : fnType.getParameters()) {
        JSType jParameterType =
            getJSType(jParameter).restrictByNotNullOrUndefined();
        if (jParameterType.isFunctionType()) {
          FunctionType jParameterFnType = jParameterType.toMaybeFunctionType();
          if (jParameterFnType.getTypeOfThis().equals(iParameterType)) {
            foundTemplateTypeOfThisParameter = true;
            // Find the actual type of the this argument.
            if (j + 1 >= childCount) {
              // TypeCheck#visitParameterList will warn so we bail.
              return;
            }
            Node jArgument = n.getChildAtIndex(j + 1);
            JSType jArgumentType = getJSType(jArgument);
            if (jArgument.isFunction() &&
                jArgumentType.isFunctionType()) {
              if (iArgumentType != null &&
                  // null and undefined get filtered out above.
                  !iArgumentType.isNoType()) {
                // If it's an function expression, update the type of this
                // using the actual type of T.
                FunctionType jArgumentFnType = jArgumentType.toMaybeFunctionType();
                if (jArgumentFnType.getTypeOfThis().isUnknownType()) {
                  // The new type will be picked up when we traverse the inner
                  // function.
                  jArgument.setJSType(
                      registry.createFunctionTypeWithNewThisType(
                          jArgumentFnType, iArgumentType));
                }
              } else {
                // Warn if the anonymous function literal references this.
                if (NodeUtil.referencesThis(
                        NodeUtil.getFunctionBody(jArgument))) {
                  compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
                      FUNCTION_LITERAL_UNDEFINED_THIS));
                }
              }
            }
            // TODO(user): Add code to TypeCheck to check that the
            // types of the arguments match.
          }
        }
        j++;
      }

      if (!foundTemplateTypeOfThisParameter) {
        compiler.report(JSError.make(NodeUtil.getSourceName(n), n,
            TEMPLATE_TYPE_OF_THIS_EXPECTED));
        return;
      }
    }
    i++;
  }
}
 
Example 9
Source File: Closure_112_TypeInference_t.java    From coming with MIT License 4 votes vote down vote up
private void maybeResolveTemplatedType(
    JSType paramType,
    JSType argType,
    Map<TemplateType, JSType> resolvedTypes) {
  if (paramType.isTemplateType()) {
    // @param {T}
    resolvedTemplateType(
        resolvedTypes, paramType.toMaybeTemplateType(), argType);
  } else if (paramType.isUnionType()) {
    // @param {Array.<T>|NodeList|Arguments|{length:number}}
    UnionType unionType = paramType.toMaybeUnionType();
    for (JSType alernative : unionType.getAlternates()) {
      maybeResolveTemplatedType(alernative, argType, resolvedTypes);
    }
  } else if (paramType.isFunctionType()) {
    FunctionType paramFunctionType = paramType.toMaybeFunctionType();
    FunctionType argFunctionType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion()
        .toMaybeFunctionType();
    if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getTypeOfThis(),
          argFunctionType.getTypeOfThis(), resolvedTypes);
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getReturnType(),
          argFunctionType.getReturnType(), resolvedTypes);
      // infer from parameter types of the function type
      maybeResolveTemplateTypeFromNodes(
          paramFunctionType.getParameters(),
          argFunctionType.getParameters(), resolvedTypes);
    }
  } else if (paramType.isTemplatizedType()) {
    // @param {Array.<T>}
    ObjectType referencedParamType = paramType
        .toMaybeTemplatizedType()
        .getReferencedType();
    JSType argObjectType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion();

    if (argObjectType.isSubtype(referencedParamType)) {
      // If the argument type is a subtype of the parameter type, resolve any
      // template types amongst their templatized types.
      TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap();
      TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap();
      for (TemplateType key : paramTypeMap.getTemplateKeys()) {
        maybeResolveTemplatedType(
            paramTypeMap.getTemplateType(key),
            argTypeMap.getTemplateType(key),
            resolvedTypes);
      }
    }
  }
}
 
Example 10
Source File: Closure_112_TypeInference_s.java    From coming with MIT License 4 votes vote down vote up
private void maybeResolveTemplatedType(
    JSType paramType,
    JSType argType,
    Map<TemplateType, JSType> resolvedTypes) {
  if (paramType.isTemplateType()) {
    // @param {T}
    resolvedTemplateType(
        resolvedTypes, paramType.toMaybeTemplateType(), argType);
  } else if (paramType.isUnionType()) {
    // @param {Array.<T>|NodeList|Arguments|{length:number}}
    UnionType unionType = paramType.toMaybeUnionType();
    for (JSType alernative : unionType.getAlternates()) {
      maybeResolveTemplatedType(alernative, argType, resolvedTypes);
    }
  } else if (paramType.isFunctionType()) {
    FunctionType paramFunctionType = paramType.toMaybeFunctionType();
    FunctionType argFunctionType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion()
        .toMaybeFunctionType();
    if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getTypeOfThis(),
          argFunctionType.getTypeOfThis(), resolvedTypes);
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getReturnType(),
          argFunctionType.getReturnType(), resolvedTypes);
      // infer from parameter types of the function type
      maybeResolveTemplateTypeFromNodes(
          paramFunctionType.getParameters(),
          argFunctionType.getParameters(), resolvedTypes);
    }
  } else if (paramType.isTemplatizedType()) {
    // @param {Array.<T>}
    ObjectType referencedParamType = paramType
        .toMaybeTemplatizedType()
        .getReferencedType();
    JSType argObjectType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion();

    if (argObjectType.isSubtype(referencedParamType)) {
      // If the argument type is a subtype of the parameter type, resolve any
      // template types amongst their templatized types.
      TemplateTypeMap paramTypeMap = paramType.getTemplateTypeMap();
      TemplateTypeMap argTypeMap = argObjectType.getTemplateTypeMap();
      for (TemplateType key : paramTypeMap.getTemplateKeys()) {
        maybeResolveTemplatedType(
            paramTypeMap.getTemplateType(key),
            argTypeMap.getTemplateType(key),
            resolvedTypes);
      }
    }
  }
}
 
Example 11
Source File: TypeInference.java    From astor with GNU General Public License v2.0 4 votes vote down vote up
private void maybeResolveTemplatedType(
    JSType paramType,
    JSType argType,
    Map<TemplateType, JSType> resolvedTypes) {
  if (paramType.isTemplateType()) {
    // @param {T}
    resolvedTemplateType(
        resolvedTypes, paramType.toMaybeTemplateType(), argType);
  } else if (paramType.isUnionType()) {
    // @param {Array.<T>|NodeList|Arguments|{length:number}}
    UnionType unionType = paramType.toMaybeUnionType();
    for (JSType alernative : unionType.getAlternates()) {
      maybeResolveTemplatedType(alernative, argType, resolvedTypes);
    }
  } else if (paramType.isFunctionType()) {
    FunctionType paramFunctionType = paramType.toMaybeFunctionType();
    FunctionType argFunctionType = argType
        .restrictByNotNullOrUndefined()
        .collapseUnion()
        .toMaybeFunctionType();
    if (argFunctionType != null && argFunctionType.isSubtype(paramType)) {
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getTypeOfThis(),
          argFunctionType.getTypeOfThis(), resolvedTypes);
      // infer from return type of the function type
      maybeResolveTemplatedType(
          paramFunctionType.getReturnType(),
          argFunctionType.getReturnType(), resolvedTypes);
      // infer from parameter types of the function type
      maybeResolveTemplateTypeFromNodes(
          paramFunctionType.getParameters(),
          argFunctionType.getParameters(), resolvedTypes);
    }
  } else if (paramType.isParameterizedType()) {
    ParameterizedType paramObjectType = paramType.toMaybeParameterizedType();
    JSType typeParameter = paramObjectType.getParameterType();
    Preconditions.checkNotNull(typeParameter);
    if (typeParameter != null) {
      // @param {Array.<T>}
      ObjectType argObjectType = argType
          .restrictByNotNullOrUndefined()
          .collapseUnion()
          .toMaybeParameterizedType();
      if (argObjectType != null && argObjectType.isSubtype(paramType)) {
        JSType argTypeParameter = argObjectType.getParameterType();
        Preconditions.checkNotNull(argTypeParameter);
        maybeResolveTemplatedType(
            typeParameter, argTypeParameter, resolvedTypes);
      }
    }
  }
}
 
Example 12
Source File: TypeInspector.java    From js-dossier with Apache License 2.0 4 votes vote down vote up
@Nullable
private TypeExpression getReturnType(
    PropertyDocs docs, Iterable<InstanceProperty> overrides, FunctionType function) {
  PropertyDocs returnDocs =
      findPropertyDocs(
          docs,
          overrides,
          input -> input != null && input.getReturnClause().getType().isPresent());

  JSType returnType = function.getReturnType();

  // Try to compensate for loss of information (how does this happen?). If the type compiler
  // says it is a templatized type, but does not have template types, or if it is an instance
  // type with a non-empty template type map, resort to JSDoc to figure out what the user wanted.
  boolean isUnknownType = returnType.isUnknownType() && !returnType.isTemplateType();
  boolean isEmptyTemplatizedType =
      returnType.isTemplatizedType() && returnType.getTemplateTypeMap().isEmpty();
  boolean isUnresolvedTemplateInstance =
      returnType.isInstanceType() && !returnType.getTemplateTypeMap().isEmpty();

  if (isUnknownType || isEmptyTemplatizedType || isUnresolvedTemplateInstance) {
    if (returnDocs != null && isKnownType(returnDocs.getJsDoc().getReturnClause())) {
      @SuppressWarnings("OptionalGetWithoutIsPresent") // Implied by isKnownType check above.
      JSTypeExpression expression = returnDocs.getJsDoc().getReturnClause().getType().get();
      returnType = evaluate(expression);
    } else {
      for (InstanceProperty property : overrides) {
        if (property.getType() != null && property.getType().isFunctionType()) {
          FunctionType fn = (FunctionType) property.getType();
          if (fn.getReturnType() != null && !fn.getReturnType().isUnknownType()) {
            returnType = fn.getReturnType();
            break;
          }
        }
      }
    }
  }

  if (returnType.isVoidType() || (returnType.isUnknownType() && !returnType.isTemplateType())) {
    return null;
  }

  NominalType context;
  if (returnDocs != null) {
    context = returnDocs.getContextType();
  } else {
    context = docs.getContextType();
  }

  TypeExpressionParser parser =
      expressionParserFactory.create(linkFactory.withTypeContext(context));
  return parser.parse(returnType);
}