diff --git a/lib/rules/destructuring-assignment.js b/lib/rules/destructuring-assignment.js index d537cca3b9..6e254f772f 100644 --- a/lib/rules/destructuring-assignment.js +++ b/lib/rules/destructuring-assignment.js @@ -181,6 +181,25 @@ module.exports = { } } + // valid-jsdoc cannot read function types + // eslint-disable-next-line valid-jsdoc + /** + * Find a parent that satisfy the given predicate + * @param {ASTNode} node + * @param {(node: ASTNode) => boolean} predicate + * @returns {ASTNode | undefined} + */ + function findParent(node, predicate) { + let n = node; + while (n) { + if (predicate(n)) { + return n; + } + n = n.parent; + } + return undefined; + } + return { FunctionDeclaration: handleStatelessComponent, @@ -196,12 +215,7 @@ module.exports = { 'FunctionExpression:exit': handleStatelessComponentExit, MemberExpression(node) { - let scope = getScope(context, node); - let SFCComponent = components.get(scope.block); - while (!SFCComponent && scope.upper && scope.upper !== scope) { - SFCComponent = components.get(scope.upper.block); - scope = scope.upper; - } + const SFCComponent = utils.getParentStatelessComponent(node); if (SFCComponent) { handleSFCUsage(node); } @@ -212,6 +226,25 @@ module.exports = { } }, + TSQualifiedName(node) { + if (configuration !== 'always') { + return; + } + // handle `typeof props.a.b` + if (node.left.type === 'Identifier' + && node.left.name === sfcParams.propsName() + && findParent(node, (n) => n.type === 'TSTypeQuery') + && utils.getParentStatelessComponent(node) + ) { + report(context, messages.useDestructAssignment, 'useDestructAssignment', { + node, + data: { + type: 'props', + }, + }); + } + }, + VariableDeclarator(node) { const classComponent = utils.getParentComponent(node); const SFCComponent = components.get(getScope(context, node).block); diff --git a/tests/lib/rules/destructuring-assignment.js b/tests/lib/rules/destructuring-assignment.js index 80bf99dd7d..9c2557c0cc 100644 --- a/tests/lib/rules/destructuring-assignment.js +++ b/tests/lib/rules/destructuring-assignment.js @@ -882,10 +882,16 @@ ${' '} }; `, options: ['always', { destructureInSignature: 'always' }], - features: ['types'], + features: ['types', 'no-babel'], errors: [ { messageId: 'useDestructAssignment', + type: 'TSQualifiedName', + data: { type: 'props' }, + }, + { + messageId: 'useDestructAssignment', + type: 'MemberExpression', data: { type: 'props' }, }, ], @@ -900,10 +906,28 @@ ${' '} }; `, options: ['always', { destructureInSignature: 'always' }], - features: ['types'], + features: ['types', 'no-babel'], errors: [ { - messageId: 'destructureInSignature', + messageId: 'useDestructAssignment', + type: 'TSQualifiedName', + data: { type: 'props' }, + }, + ], + }, + { + code: ` + function C(props: Props) { + void props.a + typeof props.b + return
+ } + `, + options: ['always'], + features: ['types', 'no-babel'], + errors: [ + { + messageId: 'useDestructAssignment', data: { type: 'props' }, }, ],