Skip to content

Commit

Permalink
[Fix] prop-types: handle nested forwardRef + memo
Browse files Browse the repository at this point in the history
Fixes #3521.

Co-authored-by: 김상두 <[email protected]>
Co-authored-by: Jordan Harband <[email protected]>
  • Loading branch information
developer-bandi and ljharb committed Jan 14, 2024
1 parent 9f4b2b9 commit 0302a2f
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 2 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [`jsx-key`]: detect conditional returns ([#3630][] @yialo)
* [`jsx-newline`]: prevent a crash when `allowMultilines ([#3633][] @ljharb)
* [`no-unknown-property`]: use a better regex to avoid a crash ([#3666][] @ljharb @SCH227)
* [`prop-types`]: handle nested forwardRef + memo ([#3679][] @developer-bandi)

### Changed
* [Refactor] `propTypes`: extract type params to var ([#3634][] @HenryBrown0)
Expand All @@ -33,6 +34,7 @@ This change log adheres to standards from [Keep a CHANGELOG](https://keepachange
* [Docs] [`jsx-key`]: fix correct example ([#3656][] @developer-bandi)
* [Tests] `jsx-wrap-multilines`: passing tests ([#3545][] @burtek)

[#3679]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3679
[#3677]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3677
[#3675]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3675
[#3674]: https://github.com/jsx-eslint/eslint-plugin-react/pull/3674
Expand Down
24 changes: 22 additions & 2 deletions lib/rules/prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ module.exports = {
while (node) {
const component = components.get(node);

const isDeclared = component && component.confidence === 2
const isDeclared = component && component.confidence >= 2
&& internalIsDeclaredInComponent(component.declaredPropTypes || {}, names);

if (isDeclared) {
Expand All @@ -176,6 +176,7 @@ module.exports = {
&& !isIgnored(propType.allNames[0])
&& !isDeclaredInComponent(component.node, propType.allNames)
));

undeclareds.forEach((propType) => {
report(context, messages.missingPropType, 'missingPropType', {
node: propType.node,
Expand All @@ -186,13 +187,32 @@ module.exports = {
});
}

/**
* @param {Object} curComponent The current component to process
* @param {Object} prevComponent The prev component to process
* @returns {Boolean} True if the component is nested False if not.
*/
function checkNestedComponent(curComponent, prevComponent) {
if (curComponent.node.callee && curComponent.node.callee.name === 'memo' && prevComponent) {
const prevComponentRange = prevComponent.node.range;
const currentComponentRange = curComponent.node.arguments[0].range;

if (prevComponentRange[0] === currentComponentRange[0]
&& prevComponentRange[1] === currentComponentRange[1]) {
return true;
}
}
return false;
}

return {
'Program:exit'() {
const list = components.list();
// Report undeclared proptypes for all classes
values(list)
.filter((component) => mustBeValidated(component))
.forEach((component) => {
.forEach((component, index, array) => {
if (checkNestedComponent(component, array[index - 1])) return;
reportUndeclaredPropTypes(component);
});
},
Expand Down
43 changes: 43 additions & 0 deletions tests/lib/rules/prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -4141,6 +4141,49 @@ ruleTester.run('prop-types', rule, {
};
`,
features: ['ts', 'no-babel'],
},
{
code: `
import React, { forwardRef, memo } from 'react';
interface Props1 {
age: number;
}
const HelloTemp = memo(({ age }: Props1) => {
return <div>Hello {age}</div>;
});
export const Hello = HelloTemp
`,
features: ['types'],
},
{
code: `
import React, { forwardRef, memo } from 'react';
interface Props1 {
age: number;
}
const HelloTemp = forwardRef(({ age }: Props1) => {
return <div>Hello {age}</div>;
});
export const Hello = memo(HelloTemp);
`,
features: ['types'],
},
{
code: `
import React, { forwardRef, memo } from 'react';
interface Props1 {
age: number;
}
export const Hello = memo(
forwardRef(({ age }: Props1) => {
return <div>Hello {age}</div>;
})
);
`,
features: ['types'],
}
)),

Expand Down

0 comments on commit 0302a2f

Please sign in to comment.