code: function makeObjectMergeAstForShapeIntersectRuntime(propTypeData) {
    const propTypeObjects = [];
    propTypeData.properties.forEach(propTypeSpec => {
      if (propTypeSpec.type === 'raw') {
        propTypeObjects.push(makeObjectAstForRaw(propTypeSpec));
      }
      else if (propTypeSpec.type === 'shape') {
        propTypeObjects.push(makeObjectAstForShape(propTypeSpec));
      }
      else if (propTypeSpec.type === 'shape-intersect-runtime') {
        // TODO: simplify all of this recursive code?
        // This returns an object.
        propTypeObjects.push(makeObjectMergeAstForShapeIntersectRuntime(propTypeSpec));
      }
    });
    const runtimeMerge = t.callExpression(
      t.memberExpression(t.identifier('Object'),
        t.identifier('assign')
      ),
      [t.objectExpression([]), ...propTypeObjects]);
    return runtimeMerge;
  }
docs: Generates AST for run-time merges involving either import variables,
  local types (shape) and other run-time merges.
  
  The returned AST is an expression that, when evaluated, returns an
  object:
  
  The expression may look like this:
  
  TODO: Does the nested case below actually work?
  
  Object.assign(
  {},
  {foo: bar},
  someImportedType === require('prop-types).any ? {} : someImportedType,
  {qux: 2},
  Object.assign(
  {},
  {nested: 2}
  ),
  {quz: require('prop-types').shape({foo: bar}),
  );
  
  This method is mainly useful when objects are actually required, such as when the
  type is participating in an intersection or when the result of the intersection is
  to be used as the main proptypes, e.g. for Foo.propTypes = {..object}.
  
  For other uses, the returned object must be wrapped in a shape. See
  makeShapeAstForShapeIntersectRuntime().
  
  @param propTypeData intermediate representation
  @returns {*}