public UnionTypeBuilder addAlternate(JSType alternate) {
    // build() returns the bottom type by default, so we can
    // just bail out early here.
    if (alternate.isNoType()) {
      return this;
    }

    isAllType = isAllType || alternate.isAllType();
    containsVoidType = containsVoidType || alternate.isVoidType();

    boolean isAlternateUnknown = alternate instanceof UnknownType;
    isNativeUnknownType = isNativeUnknownType || isAlternateUnknown;
    if (isAlternateUnknown) {
      areAllUnknownsChecked = areAllUnknownsChecked && alternate.isCheckedUnknownType();
    }
    if (!isAllType && !isNativeUnknownType) {
      if (alternate.isUnionType()) {
        addAlternates(alternate.toMaybeUnionType().getAlternates());
      } else {
        if (alternates.size() > maxUnionSize) {
          return this;
        }

        // Function types are special, because they have their
        // own bizarre sub-lattice. See the comments on
        // FunctionType#supAndInf helper and above at functionTypePosition.
        if (alternate.isFunctionType() && functionTypePosition != -1) {
          // See the comments on functionTypePosition above.
          FunctionType other =
              alternates.get(functionTypePosition).toMaybeFunctionType();
          FunctionType supremum =
              alternate.toMaybeFunctionType().supAndInfHelper(other, true);
          alternates.set(functionTypePosition, supremum);
          result = null;
          return this;
        }

        // Look through the alternates we've got so far,
        // and check if any of them are duplicates of
        // one another.
        int currentIndex = 0;
        Iterator<JSType> it = alternates.iterator();
        while (it.hasNext()) {
          boolean removeCurrent = false;
          JSType current = it.next();

          // Unknown and NoResolved types may just be names that haven't
          // been resolved yet. So keep these in the union, and just use
          // equality checking for simple de-duping.
          if (alternate.isUnknownType()
              || current.isUnknownType()
              || alternate.isNoResolvedType()
              || current.isNoResolvedType()
              || alternate.hasAnyTemplateTypes()
              || current.hasAnyTemplateTypes()) {
            if (alternate.isEquivalentTo(current, false)) {
              // Alternate is unnecessary.
              return this;
            }
          } else {

            // Because "Foo" and "Foo<?>" are roughly equivalent
            // templatized types, special care is needed when building the
            // union. For example:
            //   Object is consider a subtype of Object<string>
            // but we want to leave "Object" not "Object<string>" when
            // building the subtype.
            //

            if (alternate.isTemplatizedType() || current.isTemplatizedType()) {
              // Cases:
              // 1) alternate:Array<string> and current:Object ==> Object
              // 2) alternate:Array<string> and current:Array ==> Array
              // 3) alternate:Object<string> and
              //    current:Array ==> Array|Object<string>
              // 4) alternate:Object and current:Array<string> ==> Object
              // 5) alternate:Array and current:Array<string> ==> Array
              // 6) alternate:Array and
              //    current:Object<string> ==> Array|Object<string>
              // 7) alternate:Array<string> and
              //    current:Array<number> ==> Array<?>
              // 8) alternate:Array<string> and
              //    current:Array<string> ==> Array<string>
              // 9) alternate:Array<string> and
              //    current:Object<string> ==> Object<string>|Array<string>

              if (!current.isTemplatizedType()) {
                if (isSubtype(alternate, current)) {
                  // case 1, 2
                  return this;
                }
                // case 3: leave current, add alternate
              } else if (!alternate.isTemplatizedType()) {
                if (isSubtype(current, alternate)) {
                  // case 4, 5
                  removeCurrent = true;
                }
                // case 6: leave current, add alternate
              } else {
                checkState(current.isTemplatizedType() && alternate.isTemplatizedType());
                TemplatizedType templatizedAlternate = alternate.toMaybeTemplatizedType();
                TemplatizedType templatizedCurrent = current.toMaybeTemplatizedType();

                if (templatizedCurrent.wrapsSameRawType(templatizedAlternate)) {
                  if (alternate.getTemplateTypeMap().checkEquivalenceHelper(
                      current.getTemplateTypeMap(),
                      EquivalenceMethod.IDENTITY,
                      SubtypingMode.NORMAL)) {
                    // case 8
                    return this;
                  } else {
                    // case 7: replace with a merged alternate specialized on `?`.
                    ObjectType rawType = templatizedCurrent.getReferencedObjTypeInternal();
                    // Providing no type-parameter values specializes `rawType` on `?` by default.
                    alternate = registry.createTemplatizedType(rawType, ImmutableList.of());
                    removeCurrent = true;
                  }
                }
                // case 9: leave current, add alternate
              }
              // Otherwise leave both templatized types.
            } else if (isSubtype(alternate, current)) {
              // Alternate is unnecessary.
              mayRegisterDroppedProperties(alternate, current);
              return this;
            } else if (isSubtype(current, alternate)) {
              // Alternate makes current obsolete
              mayRegisterDroppedProperties(current, alternate);
              removeCurrent = true;
            }
          }

          if (removeCurrent) {
            it.remove();

            if (currentIndex == functionTypePosition) {
              functionTypePosition = -1;
            } else if (currentIndex < functionTypePosition) {
              functionTypePosition--;
              currentIndex--;
            }
          }
          currentIndex++;
        }

        if (alternate.isFunctionType()) {
          // See the comments on functionTypePosition above.
          checkState(functionTypePosition == -1);
          functionTypePosition = alternates.size();
        }

        alternates.add(alternate);
        result = null; // invalidate the memoized result
      }
    } else {
      result = null;
    }
    return this;
  }
public UnionTypeBuilder addAlternate ( JSType alternate ) { if ( alternate . isNoType ( ) ) { return this ; } isAllType = isAllType || alternate . isAllType ( ) ; containsVoidType = containsVoidType || alternate . isVoidType ( ) ; boolean isAlternateUnknown = alternate instanceof UnknownType ; isNativeUnknownType = isNativeUnknownType || isAlternateUnknown ; if ( isAlternateUnknown ) { areAllUnknownsChecked = areAllUnknownsChecked && alternate . isCheckedUnknownType ( ) ; } if ( ! isAllType && ! isNativeUnknownType ) { if ( alternate . isUnionType ( ) ) { addAlternates ( alternate . toMaybeUnionType ( ) . getAlternates ( ) ) ; } else { if ( alternates . size ( ) > maxUnionSize ) { return this ; } if ( alternate . isFunctionType ( ) && functionTypePosition != - 1 ) { FunctionType other = alternates . get ( functionTypePosition ) . toMaybeFunctionType ( ) ; FunctionType supremum = alternate . toMaybeFunctionType ( ) . supAndInfHelper ( other , true ) ; alternates . set ( functionTypePosition , supremum ) ; result = null ; return this ; } int currentIndex = 0 ; Iterator < JSType > it = alternates . iterator ( ) ; while ( it . hasNext ( ) ) { boolean removeCurrent = false ; JSType current = it . next ( ) ; if ( alternate . isUnknownType ( ) || current . isUnknownType ( ) || alternate . isNoResolvedType ( ) || current . isNoResolvedType ( ) || alternate . hasAnyTemplateTypes ( ) || current . hasAnyTemplateTypes ( ) ) { if ( alternate . isEquivalentTo ( current , false ) ) { return this ; } } else { if ( alternate . isTemplatizedType ( ) || current . isTemplatizedType ( ) ) { if ( ! current . isTemplatizedType ( ) ) { if ( isSubtype ( alternate , current ) ) { return this ; } } else if ( ! alternate . isTemplatizedType ( ) ) { if ( isSubtype ( current , alternate ) ) { removeCurrent = true ; } } else { checkState ( current . isTemplatizedType ( ) && alternate . isTemplatizedType ( ) ) ; TemplatizedType templatizedAlternate = alternate . toMaybeTemplatizedType ( ) ; TemplatizedType templatizedCurrent = current . toMaybeTemplatizedType ( ) ; if ( templatizedCurrent . wrapsSameRawType ( templatizedAlternate ) ) { if ( alternate . getTemplateTypeMap ( ) . checkEquivalenceHelper ( current . getTemplateTypeMap ( ) , EquivalenceMethod . IDENTITY , SubtypingMode . NORMAL ) ) { return this ; } else { ObjectType rawType = templatizedCurrent . getReferencedObjTypeInternal ( ) ; alternate = registry . createTemplatizedType ( rawType , ImmutableList . of ( ) ) ; removeCurrent = true ; } } } } else if ( isSubtype ( alternate , current ) ) { mayRegisterDroppedProperties ( alternate , current ) ; return this ; } else if ( isSubtype ( current , alternate ) ) { mayRegisterDroppedProperties ( current , alternate ) ; removeCurrent = true ; } } if ( removeCurrent ) { it . remove ( ) ; if ( currentIndex == functionTypePosition ) { functionTypePosition = - 1 ; } else if ( currentIndex < functionTypePosition ) { functionTypePosition -- ; currentIndex -- ; } } currentIndex ++ ; } if ( alternate . isFunctionType ( ) ) { checkState ( functionTypePosition == - 1 ) ; functionTypePosition = alternates . size ( ) ; } alternates . add ( alternate ) ; result = null ; } } else { result = null ; } return this ; }
Adds an alternate to the union type under construction.

<p>Returns this for easy chaining.

<p>TODO(nickreid): This method shouldn't eagerly collapse types. Doing so risks holding onto a
nested union if an alternate resolves to a union after being added. We should do use some
technique to determine if the builder has become dirty and caching the result of `build()`
until then.