src/com/google/javascript/rhino/jstype/JSType.java
/**
   * if implicitImplCache is null, there is no structural interface matching
   */
if implicitImplCache is null, there is no structural interface matching
if implicitImplCache is null there is no structural interface matching
static boolean isSubtypeHelper(JSType thisType, JSType thatType,
      ImplCache implicitImplCache, SubtypingMode subtypingMode) {
    checkNotNull(thisType);
    if (thatType.isUnknownType()) {
      return true;
    }
    if (thatType.isAllType()) {
      return true;
    }
    if (thisType.isEquivalentTo(thatType, implicitImplCache.isStructuralTyping())) {
      return true;
    }
    if (thatType.isUnionType()) {
      UnionType union = thatType.toMaybeUnionType();
      ImmutableList<JSType> alternatesWithoutStucturalTyping =
          union.getAlternatesWithoutStructuralTyping();
      for (int i = 0; i < alternatesWithoutStucturalTyping.size(); i++) {
        JSType element = alternatesWithoutStucturalTyping.get(i);
        if (thisType.isSubtype(element, implicitImplCache, subtypingMode)) {
          return true;
        }
      }
      return false;
    }
    if (subtypingMode == SubtypingMode.IGNORE_NULL_UNDEFINED
        && (thisType.isNullType() || thisType.isVoidType())) {
      return true;
    }
    TemplateTypeMap thisTypeParams = thisType.getTemplateTypeMap();
    TemplateTypeMap thatTypeParams = thatType.getTemplateTypeMap();
    boolean templateMatch = true;
    if (isBivariantType(thatType)) {
      TemplateType key = thisType.registry.getObjectElementKey();
      JSType thisElement = thisTypeParams.getResolvedTemplateType(key);
      JSType thatElement = thatTypeParams.getResolvedTemplateType(key);
      templateMatch = thisElement.isSubtype(thatElement, implicitImplCache, subtypingMode)
          || thatElement.isSubtype(thisElement, implicitImplCache, subtypingMode);
    } else if (isIThenableSubtype(thatType)) {
      TemplateType key = thisType.registry.getThenableValueKey();
      JSType thisElement = thisTypeParams.getResolvedTemplateType(key);
      JSType thatElement = thatTypeParams.getResolvedTemplateType(key);
      templateMatch = thisElement.isSubtype(thatElement, implicitImplCache, subtypingMode);
    } else {
      templateMatch = thisTypeParams.checkEquivalenceHelper(
          thatTypeParams, EquivalenceMethod.INVARIANT, subtypingMode);
    }
    if (!templateMatch) {
      return false;
    }
    if (implicitImplCache.isStructuralTyping()
        && thisType.isObject() && thatType.isStructuralType()) {
      return thisType.toMaybeObjectType().isStructuralSubtype(
          thatType.toMaybeObjectType(), implicitImplCache, subtypingMode);
    }
    if (thisType.isTemplatizedType()) {
      return thisType.toMaybeTemplatizedType().getReferencedType().isSubtype(
          thatType, implicitImplCache, subtypingMode);
    }
    if (thatType instanceof ProxyObjectType) {
      return thisType.isSubtype(
          ((ProxyObjectType) thatType).getReferencedTypeInternal(),
          implicitImplCache, subtypingMode);
    }
    return false;
  }
static boolean isSubtypeHelper ( JSType thisType , JSType thatType , ImplCache implicitImplCache , SubtypingMode subtypingMode ) { checkNotNull ( thisType ) ; if ( thatType . isUnknownType ( ) ) { return true ; } if ( thatType . isAllType ( ) ) { return true ; } if ( thisType . isEquivalentTo ( thatType , implicitImplCache . isStructuralTyping ( ) ) ) { return true ; } if ( thatType . isUnionType ( ) ) { UnionType union = thatType . toMaybeUnionType ( ) ; ImmutableList < JSType > alternatesWithoutStucturalTyping = union . getAlternatesWithoutStructuralTyping ( ) ; for ( int i = 0 ; i < alternatesWithoutStucturalTyping . size ( ) ; i ++ ) { JSType element = alternatesWithoutStucturalTyping . get ( i ) ; if ( thisType . isSubtype ( element , implicitImplCache , subtypingMode ) ) { return true ; } } return false ; } if ( subtypingMode == SubtypingMode . IGNORE_NULL_UNDEFINED && ( thisType . isNullType ( ) || thisType . isVoidType ( ) ) ) { return true ; } TemplateTypeMap thisTypeParams = thisType . getTemplateTypeMap ( ) ; TemplateTypeMap thatTypeParams = thatType . getTemplateTypeMap ( ) ; boolean templateMatch = true ; if ( isBivariantType ( thatType ) ) { TemplateType key = thisType . registry . getObjectElementKey ( ) ; JSType thisElement = thisTypeParams . getResolvedTemplateType ( key ) ; JSType thatElement = thatTypeParams . getResolvedTemplateType ( key ) ; templateMatch = thisElement . isSubtype ( thatElement , implicitImplCache , subtypingMode ) || thatElement . isSubtype ( thisElement , implicitImplCache , subtypingMode ) ; } else if ( isIThenableSubtype ( thatType ) ) { TemplateType key = thisType . registry . getThenableValueKey ( ) ; JSType thisElement = thisTypeParams . getResolvedTemplateType ( key ) ; JSType thatElement = thatTypeParams . getResolvedTemplateType ( key ) ; templateMatch = thisElement . isSubtype ( thatElement , implicitImplCache , subtypingMode ) ; } else { templateMatch = thisTypeParams . checkEquivalenceHelper ( thatTypeParams , EquivalenceMethod . INVARIANT , subtypingMode ) ; } if ( ! templateMatch ) { return false ; } if ( implicitImplCache . isStructuralTyping ( ) && thisType . isObject ( ) && thatType . isStructuralType ( ) ) { return thisType . toMaybeObjectType ( ) . isStructuralSubtype ( thatType . toMaybeObjectType ( ) , implicitImplCache , subtypingMode ) ; } if ( thisType . isTemplatizedType ( ) ) { return thisType . toMaybeTemplatizedType ( ) . getReferencedType ( ) . isSubtype ( thatType , implicitImplCache , subtypingMode ) ; } if ( thatType instanceof ProxyObjectType ) { return thisType . isSubtype ( ( ( ProxyObjectType ) thatType ) . getReferencedTypeInternal ( ) , implicitImplCache , subtypingMode ) ; } return false ; }
if implicitImplCache is null, there is no structural interface matching
if implicitImplCache is null there is no structural interface matching
// unknown // all type // equality // unions // use an indexed for-loop to avoid allocations // TemplateTypeMaps. This check only returns false if the TemplateTypeMaps // are not equivalent. // Array and Object are exempt from template type invariance; their // template types maps are bivariant.  That is they are considered // a match if either ObjectElementKey values are subtypes of the // other. // NOTE: special case IThenable subclass (Promise, etc).  These classes are expected to be // covariant. // Also note that this ignores any additional template type arguments that might be defined // on subtypes.  If we expand this to other types, a more correct solution will be needed. // If the super type is a structural type, then we can't safely remove a templatized type // (since it might affect the types of the properties) // Templatized types. For nominal types, the above check guarantees TemplateTypeMap // equivalence; check if the base type is a subtype. // proxy types