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); } }
/** * Computes the greatest subtype of two related templatized types. * * @return The greatest subtype. */ JSType getGreatestSubtypeHelper(JSType rawThat) { Preconditions.checkNotNull(rawThat); if (!wrapsSameRawType(rawThat)) { if (!rawThat.isTemplatizedType()) { if (this.isSubtype(rawThat)) { return this; } else if (rawThat.isSubtype(this)) { return filterNoResolvedType(rawThat); } } if (this.isObject() && rawThat.isObject()) { return this.getNativeType(JSTypeNative.NO_OBJECT_TYPE); } return this.getNativeType(JSTypeNative.NO_TYPE); } TemplatizedType that = rawThat.toMaybeTemplatizedType(); Preconditions.checkNotNull(that); if (getTemplateTypeMap() .checkEquivalenceHelper( that.getTemplateTypeMap(), EquivalenceMethod.INVARIANT, SubtypingMode.NORMAL)) { return this; } // For types that have the same raw type but different type parameters, // we simply create a type has a "unknown" type parameter. This is // equivalent to the raw type. return getReferencedObjTypeInternal(); }
@Override public boolean isSubtype(JSType that) { if (JSType.isSubtype(this, that)) { return true; } else { return that.isObject() && !that.isNoType() && !that.isNoResolvedType(); } }
/** * 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(); }
@Override public TernaryValue testForEquality(JSType that) { TernaryValue result = super.testForEquality(that); if (result != null) { return result; } if (that.isUnknownType() || that.isSubtype(getNativeType(JSTypeNative.OBJECT_NUMBER_STRING_BOOLEAN))) { return UNKNOWN; } return FALSE; }
@Override public JSType getLeastSupertype(JSType that) { if (!that.isUnknownType() && !that.isUnionType()) { for (JSType alternate : alternatesWithoutStucturalTyping) { if (!alternate.isUnknownType() && that.isSubtype(alternate)) { return this; } } } return getLeastSupertype(this, that); }
private boolean isSubtype(JSType rightType, JSType leftType, boolean isStructural) { // if thisType or thatType is an unresolved templatized type, // then there is no structural interface matching boolean thisUnresolved = rightType.isTemplatizedType() && !rightType.toMaybeTemplatizedType().isResolved(); boolean thatUnresolved = leftType.isTemplatizedType() && !leftType.toMaybeTemplatizedType().isResolved(); if (isStructural && !thisUnresolved && !thatUnresolved) { return rightType.isSubtype(leftType); } else { return rightType.isSubtypeWithoutStructuralTyping(leftType); } }
@Override protected boolean isSubtype(JSType that, ImplCache implicitImplCache) { // unknown if (that.isUnknownType()) { return true; } // all type if (that.isAllType()) { return true; } for (JSType element : alternatesWithoutStucturalTyping) { if (!element.isSubtype(that, implicitImplCache)) { return false; } } return true; }
/** Determines if typeA is a subtype of typeB */ static boolean isSubtype(ObjectType typeA, RecordType typeB) { // typeA is a subtype of record type typeB iff: // 1) typeA has all the properties declared in typeB. // 2) And for each property of typeB, // 2a) if the property of typeA is declared, it must be equal // to the type of the property of typeB, // 2b) otherwise, it must be a subtype of the property of typeB. // // To figure out why this is true, consider the following pseudo-code: // /** @type {{a: (Object,null)}} */ var x; // /** @type {{a: !Object}} */ var y; // var z = {a: {}}; // x.a = null; // // y cannot be assigned to x, because line 4 would violate y's declared // properties. But z can be assigned to x. Even though z and y are the // same type, the properties of z are inferred--and so an assignment // to the property of z would not violate any restrictions on it. for (String property : typeB.getOwnPropertyNames()) { if (!typeA.hasProperty(property)) { return false; } JSType propA = typeA.getPropertyType(property); JSType propB = typeB.getPropertyType(property); if (typeA.isPropertyTypeDeclared(property)) { // If one declared property isn't invariant, // then the whole record isn't covariant. if (!propA.isInvariant(propB)) { return false; } } else { // If one inferred property isn't a subtype, // then the whole record isn't covariant. if (!propA.isSubtype(propB)) { return false; } } } return true; }