Skip to content

Commit

Permalink
Support object spread in the type checker.
Browse files Browse the repository at this point in the history
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=229439222
  • Loading branch information
johnplaisted authored and lauraharker committed Jan 16, 2019
1 parent c8dc037 commit f66ff18
Show file tree
Hide file tree
Showing 5 changed files with 563 additions and 15 deletions.
12 changes: 11 additions & 1 deletion src/com/google/javascript/jscomp/TypeCheck.java
Expand Up @@ -1006,6 +1006,11 @@ public void visit(NodeTraversal t, Node n, Node parent) {
} }


private void checkSpread(NodeTraversal t, Node spreadNode) { private void checkSpread(NodeTraversal t, Node spreadNode) {
if (spreadNode.getParent().isObjectLit()) {
// Nothing to check for object spread, anything can be spread.
return;
}

Node iterableNode = spreadNode.getOnlyChild(); Node iterableNode = spreadNode.getOnlyChild();
ensureTyped(iterableNode); ensureTyped(iterableNode);
JSType iterableType = getJSType(iterableNode); JSType iterableType = getJSType(iterableNode);
Expand Down Expand Up @@ -1328,7 +1333,7 @@ private void visitObjectPattern(NodeTraversal t, Node pattern) {
* we change the schema of the object type it is referring to. * we change the schema of the object type it is referring to.
* *
* @param t the traversal * @param t the traversal
* @param key the ASSIGN, STRING_KEY, MEMBER_FUNCTION_DEF, or COMPUTED_PROPERTY node * @param key the ASSIGN, STRING_KEY, MEMBER_FUNCTION_DEF, SPREAD, or COMPUTED_PROPERTY node
* @param owner the parent node, either OBJECTLIT or CLASS_MEMBERS * @param owner the parent node, either OBJECTLIT or CLASS_MEMBERS
* @param ownerType the instance type of the enclosing object/class * @param ownerType the instance type of the enclosing object/class
*/ */
Expand Down Expand Up @@ -1371,6 +1376,11 @@ private void visitObjectOrClassLiteralKey(
} }
} }


if (key.isSpread()) {
// Rely on type inference to figure out what the key/types this adds to this object.
return;
}

// TODO(johnlenz): Validate get and set function declarations are valid // TODO(johnlenz): Validate get and set function declarations are valid
// as is the functions can have "extraneous" bits. // as is the functions can have "extraneous" bits.


Expand Down
50 changes: 38 additions & 12 deletions src/com/google/javascript/jscomp/TypeInference.java
Expand Up @@ -1333,26 +1333,52 @@ private FlowScope traverseObjectLiteral(Node n, FlowScope scope) {
return scope; return scope;
} }


String qObjName = NodeUtil.getBestLValueName( String qObjName = NodeUtil.getBestLValueName(NodeUtil.getBestLValue(n));
NodeUtil.getBestLValue(n)); for (Node key = n.getFirstChild(); key != null; key = key.getNext()) {
for (Node name = n.getFirstChild(); name != null; if (key.isComputedProp()) {
name = name.getNext()) {
if (name.isComputedProp()) {
// Don't define computed properties as inferred properties on the object // Don't define computed properties as inferred properties on the object
continue; continue;
} }
String memberName = NodeUtil.getObjectLitKeyName(name);
if (key.isSpread()) {
Node name = key.getFirstChild();
JSType nameType = name.getJSType();

if (nameType == null) {
continue;
}

ObjectType spreadType = nameType.toMaybeObjectType();

while (spreadType != null) {
Set<String> spreadPropertyNames = spreadType.getOwnPropertyNames();
for (String propertyName : spreadPropertyNames) {
objectType.defineInferredProperty(
propertyName, spreadType.getPropertyType(propertyName), key);
}
if ((!spreadType.isConstructor()
&& !spreadType.isInterface()
&& !spreadType.isInstanceType())
|| spreadType.getSuperClassConstructor() == null) {
break;
}
spreadType = spreadType.getSuperClassConstructor().getInstanceType();
}

continue;
}

String memberName = NodeUtil.getObjectLitKeyName(key);
if (memberName != null) { if (memberName != null) {
JSType rawValueType = name.getFirstChild().getJSType(); JSType rawValueType = key.getFirstChild().getJSType();
JSType valueType = JSType valueType = TypeCheck.getObjectLitKeyTypeFromValueType(key, rawValueType);
TypeCheck.getObjectLitKeyTypeFromValueType(name, rawValueType);
if (valueType == null) { if (valueType == null) {
valueType = unknownType; valueType = unknownType;
} }
objectType.defineInferredProperty(memberName, valueType, name); objectType.defineInferredProperty(memberName, valueType, key);


// Do normal flow inference if this is a direct property assignment. // Do normal flow inference if this is a direct property assignment.
if (qObjName != null && name.isStringKey()) { if (qObjName != null && key.isStringKey()) {
String qKeyName = qObjName + "." + memberName; String qKeyName = qObjName + "." + memberName;
TypedVar var = getDeclaredVar(scope, qKeyName); TypedVar var = getDeclaredVar(scope, qKeyName);
JSType oldType = var == null ? null : var.getType(); JSType oldType = var == null ? null : var.getType();
Expand All @@ -1362,7 +1388,7 @@ private FlowScope traverseObjectLiteral(Node n, FlowScope scope) {


scope = scope =
scope.inferQualifiedSlot( scope.inferQualifiedSlot(
name, qKeyName, oldType == null ? unknownType : oldType, valueType, false); key, qKeyName, oldType == null ? unknownType : oldType, valueType, false);
} }
} else { } else {
n.setJSType(unknownType); n.setJSType(unknownType);
Expand Down
6 changes: 4 additions & 2 deletions src/com/google/javascript/jscomp/TypedScopeCreator.java
Expand Up @@ -750,8 +750,10 @@ void processObjectLitProperties(
Node objLit, ObjectType objLitType, Node objLit, ObjectType objLitType,
boolean declareOnOwner) { boolean declareOnOwner) {
for (Node keyNode = objLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) { for (Node keyNode = objLit.getFirstChild(); keyNode != null; keyNode = keyNode.getNext()) {
if (keyNode.isComputedProp()) { if (keyNode.isComputedProp() || keyNode.isSpread()) {
// Don't try defining computed properties on an object. // Don't try defining computed or spread properties on an object. Note that for spread
// type inference will try to determine the properties and types. We cannot do it here as
// we don't have all the type information of the spread object.
continue; continue;
} }
Node value = keyNode.getFirstChild(); Node value = keyNode.getFirstChild();
Expand Down

0 comments on commit f66ff18

Please sign in to comment.