JSType meet(JSType that) {
    UnionTypeBuilder builder = new UnionTypeBuilder(registry);
    for (JSType alternate : alternatesWithoutStucturalTyping) {
      if (alternate.isSubtype(that)) {
        builder.addAlternate(alternate);
      }
    }

    if (that.isUnionType()) {
      for (JSType otherAlternate : that.toMaybeUnionType().alternatesWithoutStucturalTyping) {
        if (otherAlternate.isSubtype(this)) {
          builder.addAlternate(otherAlternate);
        }
      }
    } else if (that.isSubtype(this)) {
      builder.addAlternate(that);
    }
    JSType result = builder.build();
    if (!result.isNoType()) {
      return result;
    } else if (this.isObject() && (that.isObject() && !that.isNoType())) {
      return getNativeType(JSTypeNative.NO_OBJECT_TYPE);
    } else {
      return getNativeType(JSTypeNative.NO_TYPE);
    }
  }
 @Override
 public JSType restrictByNotNullOrUndefined() {
   UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
   for (JSType t : alternatesWithoutStucturalTyping) {
     restricted.addAlternate(t.restrictByNotNullOrUndefined());
   }
   return restricted.build();
 }
 @Override
 public JSType autobox() {
   UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
   for (JSType t : alternatesWithoutStucturalTyping) {
     restricted.addAlternate(t.autobox());
   }
   return restricted.build();
 }
 @Override
 public JSType getRestrictedTypeGivenToBooleanOutcome(boolean outcome) {
   // gather elements after restriction
   UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
   for (JSType element : alternatesWithoutStucturalTyping) {
     restricted.addAlternate(element.getRestrictedTypeGivenToBooleanOutcome(outcome));
   }
   return restricted.build();
 }
 /**
  * Returns a more restricted union type than {@code this} one, in which all subtypes of {@code
  * type} have been removed.
  *
  * <p>Examples:
  *
  * <ul>
  *   <li>{@code (number,string)} restricted by {@code number} is {@code string}
  *   <li>{@code (null, EvalError, URIError)} restricted by {@code Error} is {@code null}
  * </ul>
  *
  * @param type the supertype of the types to remove from this union type
  */
 public JSType getRestrictedUnion(JSType type) {
   UnionTypeBuilder restricted = new UnionTypeBuilder(registry);
   for (JSType t : alternatesWithoutStucturalTyping) {
     // Keep all unknown/unresolved types.
     if (t.isUnknownType() || t.isNoResolvedType() || !t.isSubtype(type)) {
       restricted.addAlternate(t);
     }
   }
   return restricted.build();
 }
  JSType getGreatestSubtypeHelper(JSType that) {
    if (that.isRecordType()) {
      RecordType thatRecord = that.toMaybeRecordType();
      RecordTypeBuilder builder = new RecordTypeBuilder(registry);
      builder.setSynthesized(true);

      // The greatest subtype consists of those *unique* properties of both
      // record types. If any property conflicts, then the NO_TYPE type
      // is returned.
      for (String property : getOwnPropertyNames()) {
        if (thatRecord.hasProperty(property)
            && !thatRecord.getPropertyType(property).isInvariant(getPropertyType(property))) {
          return registry.getNativeObjectType(JSTypeNative.NO_TYPE);
        }

        builder.addProperty(property, getPropertyType(property), getPropertyNode(property));
      }

      for (String property : thatRecord.getOwnPropertyNames()) {
        if (!hasProperty(property)) {
          builder.addProperty(
              property, thatRecord.getPropertyType(property), thatRecord.getPropertyNode(property));
        }
      }

      return builder.build();
    }

    JSType greatestSubtype = registry.getNativeType(JSTypeNative.NO_OBJECT_TYPE);
    JSType thatRestrictedToObj =
        registry.getNativeType(JSTypeNative.OBJECT_TYPE).getGreatestSubtype(that);
    if (!thatRestrictedToObj.isEmptyType()) {
      // In this branch, the other type is some object type. We find
      // the greatest subtype with the following algorithm:
      // 1) For each property "x" of this record type, take the union
      //    of all classes with a property "x" with a compatible property type.
      //    and which are a subtype of {@code that}.
      // 2) Take the intersection of all of these unions.
      for (String propName : getOwnPropertyNames()) {
        JSType propType = getPropertyType(propName);
        UnionTypeBuilder builder = new UnionTypeBuilder(registry);
        for (ObjectType alt : registry.getEachReferenceTypeWithProperty(propName)) {
          JSType altPropType = alt.getPropertyType(propName);
          if (altPropType != null
              && !alt.isEquivalentTo(this)
              && alt.isSubtype(that)
              && propType.isInvariant(altPropType)) {
            builder.addAlternate(alt);
          }
        }
        greatestSubtype = greatestSubtype.getLeastSupertype(builder.build());
      }
    }
    return greatestSubtype;
  }
  /**
   * Creates a union type.
   *
   * @param alternatesWithoutStructuralTyping the alternates of the union without structural typing
   *     subtype
   */
  UnionType(JSTypeRegistry registry, Collection<JSType> alternatesWithoutStructuralTyping) {
    super(registry);
    this.alternatesWithoutStucturalTyping = alternatesWithoutStructuralTyping;

    UnionTypeBuilder builder = new UnionTypeBuilder(registry);
    for (JSType alternate : alternatesWithoutStructuralTyping) {
      builder.addAlternate(alternate, true);
    }
    this.alternates = builder.getAlternates();
    this.hashcode = this.alternatesWithoutStucturalTyping.hashCode();
  }
 /**
  * Use UnionTypeBuilder to rebuild the list of alternates and hashcode of the current UnionType.
  */
 private void rebuildAlternates() {
   UnionTypeBuilder builder = new UnionTypeBuilder(registry);
   for (JSType alternate : alternatesWithoutStucturalTyping) {
     builder.addAlternate(alternate);
   }
   alternatesWithoutStucturalTyping = builder.getAlternates();
   builder = new UnionTypeBuilder(registry);
   for (JSType alternate : alternatesWithoutStucturalTyping) {
     builder.addAlternate(alternate, true);
   }
   alternates = builder.getAlternates();
   hashcode = alternatesWithoutStucturalTyping.hashCode();
 }
 @Override
 public TypePair getTypesUnderShallowInequality(JSType that) {
   UnionTypeBuilder thisRestricted = new UnionTypeBuilder(registry);
   UnionTypeBuilder thatRestricted = new UnionTypeBuilder(registry);
   for (JSType element : alternatesWithoutStucturalTyping) {
     TypePair p = element.getTypesUnderShallowInequality(that);
     if (p.typeA != null) {
       thisRestricted.addAlternate(p.typeA);
     }
     if (p.typeB != null) {
       thatRestricted.addAlternate(p.typeB);
     }
   }
   return new TypePair(thisRestricted.build(), thatRestricted.build());
 }