Skip to content

Commit fb81def

Browse files
committed
[Fix] destructuring-assignment: fix false negative when using typeof props.a
Fixes jsx-eslint#3828
1 parent c23d549 commit fb81def

File tree

2 files changed

+49
-9
lines changed

2 files changed

+49
-9
lines changed

lib/rules/destructuring-assignment.js

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,25 @@ module.exports = {
181181
}
182182
}
183183

184+
// valid-jsdoc cannot read function types
185+
// eslint-disable-next-line valid-jsdoc
186+
/**
187+
* Find a parent that satisfy the given predicate
188+
* @param {ASTNode} node
189+
* @param {(node: ASTNode) => boolean} predicate
190+
* @returns {ASTNode | undefined}
191+
*/
192+
function findParent(node, predicate) {
193+
let n = node;
194+
while (n) {
195+
if (predicate(n)) {
196+
return n;
197+
}
198+
n = n.parent;
199+
}
200+
return undefined;
201+
}
202+
184203
return {
185204

186205
FunctionDeclaration: handleStatelessComponent,
@@ -196,12 +215,7 @@ module.exports = {
196215
'FunctionExpression:exit': handleStatelessComponentExit,
197216

198217
MemberExpression(node) {
199-
let scope = getScope(context, node);
200-
let SFCComponent = components.get(scope.block);
201-
while (!SFCComponent && scope.upper && scope.upper !== scope) {
202-
SFCComponent = components.get(scope.upper.block);
203-
scope = scope.upper;
204-
}
218+
const SFCComponent = utils.getParentStatelessComponent(node);
205219
if (SFCComponent) {
206220
handleSFCUsage(node);
207221
}
@@ -212,6 +226,25 @@ module.exports = {
212226
}
213227
},
214228

229+
TSQualifiedName(node) {
230+
if (configuration !== 'always') {
231+
return;
232+
}
233+
// handle `typeof props.a.b`
234+
if (node.left.type === 'Identifier'
235+
&& node.left.name === sfcParams.propsName()
236+
&& findParent(node, (n) => n.type === 'TSTypeQuery')
237+
&& utils.getParentStatelessComponent(node)
238+
) {
239+
report(context, messages.useDestructAssignment, 'useDestructAssignment', {
240+
node,
241+
data: {
242+
type: 'props',
243+
},
244+
});
245+
}
246+
},
247+
215248
VariableDeclarator(node) {
216249
const classComponent = utils.getParentComponent(node);
217250
const SFCComponent = components.get(getScope(context, node).block);

tests/lib/rules/destructuring-assignment.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -882,10 +882,16 @@ ${' '}
882882
};
883883
`,
884884
options: ['always', { destructureInSignature: 'always' }],
885-
features: ['types'],
885+
features: ['types', 'no-babel'],
886886
errors: [
887887
{
888888
messageId: 'useDestructAssignment',
889+
type: 'TSQualifiedName',
890+
data: { type: 'props' },
891+
},
892+
{
893+
messageId: 'useDestructAssignment',
894+
type: 'MemberExpression',
889895
data: { type: 'props' },
890896
},
891897
],
@@ -900,10 +906,11 @@ ${' '}
900906
};
901907
`,
902908
options: ['always', { destructureInSignature: 'always' }],
903-
features: ['types'],
909+
features: ['types', 'no-babel'],
904910
errors: [
905911
{
906-
messageId: 'destructureInSignature',
912+
messageId: 'useDestructAssignment',
913+
type: 'TSQualifiedName',
907914
data: { type: 'props' },
908915
},
909916
],

0 commit comments

Comments
 (0)