Repository: yannickcr/eslint-plugin-react (js)

Issue:
[prop-types] nested flow object type spread crashes
Essentially the same presentation as #1178, but slightly different (nested or alias) case as proven by PR #1254.

-----

```js
Cannot read property 'type' of undefined
TypeError: Cannot read property 'type' of undefined
    at buildTypeAnnotationDeclarationTypes (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:478:25)
    at /Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:490:54
    at iterateProperties (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:359:11)
    at buildTypeAnnotationDeclarationTypes (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:489:11)
    at /Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:712:38
    at iterateProperties (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:359:11)
    at markPropTypesAsDeclared (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:707:11)
    at Object.ClassDeclaration (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/rules/prop-types.js:876:11)
    at Linter.updatedRuleInstructions.(anonymous function) (/Users/kross/projects/ui/node_modules/eslint-plugin-react/lib/util/Components.js:624:75)
    at emitOne (events.js:101:20)
    at Linter.emit (events.js:191:7)

Process finished with exit code 1
```

I finally debugged my case close enough to add a test that breaks the current `master`. 

```js
{
      code: [
        'import type {BasePerson} from \'./types\'',
        'type Props = {',
        '  person: {',
        '   ...$Exact<BasePerson>',
        '   lastname: string',
        '  }',
        '};',
        'class Hello extends React.Component {',
        '  props: Props;',
        '  render () {',
        '    return <div>Hello {this.props.person.firstname}</div>;',
        '  }',
        '}'
      ].join('\n'),
      parser: 'babel-eslint'
    }
```

Pull request description:
Fix nested object type spread
Closes #1255 

Object type spreads are ignored, but only at the top level.  This ignores object type spreads at any level.

Patch:
diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js
index 6d2d57aa..17809767 100644
--- a/lib/rules/prop-types.js
+++ b/lib/rules/prop-types.js
@@ -480,13 +480,23 @@ module.exports = {
           }
           return true;
         case 'ObjectTypeAnnotation':
+          let containsObjectTypeSpread = false;
           var shapeTypeDefinition = {
             type: 'shape',
             children: {}
           };
           iterateProperties(annotation.properties, function(childKey, childValue) {
-            shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue);
+            if (!childKey && !childValue) {
+              containsObjectTypeSpread = true;
+            } else {
+              shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue);
+            }
           });
+
+          // nested object type spread means we need to ignore/accept everything in this object
+          if (containsObjectTypeSpread) {
+            return true;
+          }
           return shapeTypeDefinition;
         case 'UnionTypeAnnotation':
           var unionTypeDefinition = {


Test patch:
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index 7feadfb2..95194d93 100644
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -1414,6 +1414,67 @@ ruleTester.run('prop-types', rule, {
         '}'
       ].join('\n'),
       parser: 'babel-eslint'
+    }, {
+      code: [
+        'type Person = {',
+        '  ...$Exact<data>,',
+        '  lastname: string',
+        '};',
+        'class Hello extends React.Component {',
+        '  props: Person;',
+        '  render () {',
+        '    return <div>Hello {this.props.firstname}</div>;',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint'
+    }, {
+      code: [
+        'import type {Data} from \'./Data\'',
+        'type Person = {',
+        '  ...Data,',
+        '  lastname: string',
+        '};',
+        'class Hello extends React.Component {',
+        '  props: Person;',
+        '  render () {',
+        '    return <div>Hello {this.props.bar}</div>;',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint'
+    }, {
+      code: [
+        'import type {Data} from \'some-libdef-like-flow-typed-provides\'',
+        'type Person = {',
+        '  ...Data,',
+        '  lastname: string',
+        '};',
+        'class Hello extends React.Component {',
+        '  props: Person;',
+        '  render () {',
+        '    return <div>Hello {this.props.bar}</div>;',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint'
+    }, {
+      code: [
+        'import type {BasePerson} from \'./types\'',
+        'type Props = {',
+        '  person: {',
+        '   ...$Exact<BasePerson>,',
+        '   lastname: string',
+        '  }',
+        '};',
+        'class Hello extends React.Component {',
+        '  props: Props;',
+        '  render () {',
+        '    return <div>Hello {this.props.person.firstname}</div>;',
+        '  }',
+        '}'
+      ].join('\n'),
+      parser: 'babel-eslint'
     }, {
       code: [
         'type Person = {',