Repository: jsx-eslint/eslint-plugin-react (JavaScript)

Issue:
prop-types not enforced with destructured props and default empty object param
```
// complains about 'baz' missing from prop-types validation
const Component = ({ foo, bar, baz }) => (
  // ...JSX
);

// doesn't complain about 'baz' missing from prop-types validation
const Component = ({ foo, bar, baz } = {}) => (
  // ...JSX
);

Component.propTypes = {
  foo: PropTypes.string,
  bar: PropTypes.string,
};
```

Since it's generally best-practice to provide defaults when destructuring, it would be nice if passing an empty object as a default param didn't break prop-types validation.


Discussion:

Patch:
diff --git a/lib/util/usedPropTypes.js b/lib/util/usedPropTypes.js
index 85cc51e076..aea9f8093e 100755
--- a/lib/util/usedPropTypes.js
+++ b/lib/util/usedPropTypes.js
@@ -316,16 +316,17 @@ module.exports = function usedPropTypesInstructions(context, components, utils)
         break;
       case 'ArrowFunctionExpression':
       case 'FunctionDeclaration':
-      case 'FunctionExpression':
+      case 'FunctionExpression': {
         if (node.params.length === 0) {
           break;
         }
         type = 'destructuring';
-        properties = node.params[0].properties;
-        if (inSetStateUpdater()) {
-          properties = node.params[1].properties;
-        }
+        const propParam = inSetStateUpdater() ? node.params[1] : node.params[0];
+        properties = propParam.type === 'AssignmentPattern'
+          ? propParam.left.properties
+          : propParam.properties;
         break;
+      }
       case 'VariableDeclarator':
         for (let i = 0, j = node.id.properties.length; i < j; i++) {
           // let {props: {firstname}} = this
@@ -421,7 +422,13 @@ module.exports = function usedPropTypesInstructions(context, components, utils)
    *   FunctionDeclaration, or FunctionExpression
    */
   function markDestructuredFunctionArgumentsAsUsed(node) {
-    const destructuring = node.params && node.params[0] && node.params[0].type === 'ObjectPattern';
+    const param = node.params && inSetStateUpdater() ? node.params[1] : node.params[0];
+
+    const destructuring = param && (
+      param.type === 'ObjectPattern' ||
+      param.type === 'AssignmentPattern' && param.left.type === 'ObjectPattern'
+    );
+
     if (destructuring && (components.get(node) || components.get(node.parent))) {
       markPropTypesAsUsed(node);
     }


Test patch:
diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js
index 77bda59f17..851b8d4386 100755
--- a/tests/lib/rules/prop-types.js
+++ b/tests/lib/rules/prop-types.js
@@ -3393,6 +3393,16 @@ ruleTester.run('prop-types', rule, {
       errors: [
         {message: '\'bar\' is missing in props validation'}
       ]
+    }, {
+      code: [
+        'function SomeComponent({bar} = baz) {',
+        '  function f({foo}) {}',
+        '  return <div className={f()}>{bar}</div>;',
+        '}'
+      ].join('\n'),
+      errors: [
+        {message: '\'bar\' is missing in props validation'}
+      ]
     }, {
       code: [
         'class Hello extends React.PureComponent {',
@@ -4226,6 +4236,21 @@ ruleTester.run('prop-types', rule, {
         message: '\'bar\' is missing in props validation'
       }]
     },
+    {
+      code: `
+        class Foo extends React.Component {
+          setZoo() {
+            this.setState((state, {zoo}) => ({ zoo }));
+          }
+          render() {
+            return <div />;
+          }
+        }
+      `,
+      errors: [{
+        message: '\'zoo\' is missing in props validation'
+      }]
+    },
     {
       code: `
         class Foo extends React.Component {