/** * Given an interface and a property, finds the top-most super interface that has the property * defined (including this interface). */ public static ObjectType getTopDefiningInterface(ObjectType type, String propertyName) { ObjectType foundType = null; if (type.hasProperty(propertyName)) { foundType = type; } for (ObjectType interfaceType : type.getCtorExtendedInterfaces()) { if (interfaceType.hasProperty(propertyName)) { foundType = getTopDefiningInterface(interfaceType, propertyName); } } return foundType; }
/** Gets the number of properties of this object. */ @Override public int getPropertiesCount() { ObjectType implicitPrototype = getImplicitPrototype(); if (implicitPrototype == null) { return this.properties.size(); } int localCount = 0; for (String property : properties.keySet()) { if (!implicitPrototype.hasProperty(property)) { localCount++; } } return implicitPrototype.getPropertiesCount() + localCount; }
/** 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; }