Skip to content

Commit 97ac0fa

Browse files
jzabalaljharb
authored andcommitted
[Fix] no-unused-prop-types: improve component declared props detection
Fixes #2752.
1 parent 5029bd1 commit 97ac0fa

File tree

5 files changed

+123
-9
lines changed

5 files changed

+123
-9
lines changed

lib/util/Components.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const propTypesUtil = require('./propTypes');
1616
const jsxUtil = require('./jsx');
1717
const usedPropTypesUtil = require('./usedPropTypes');
1818
const defaultPropsUtil = require('./defaultProps');
19+
const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
1920

2021
function getId(node) {
2122
return node && node.range.join(':');
@@ -70,14 +71,6 @@ function isReturnsLogicalJSX(node, property, strict) {
7071
: (returnsLogicalJSXLeft || returnsLogicalJSXRight);
7172
}
7273

73-
function isFirstLetterCapitalized(word) {
74-
if (!word) {
75-
return false;
76-
}
77-
const firstLetter = word.charAt(0);
78-
return firstLetter.toUpperCase() === firstLetter;
79-
}
80-
8174
const Lists = new WeakMap();
8275

8376
/**

lib/util/isFirstLetterCapitalized.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
'use strict';
2+
3+
/**
4+
* Check if the first letter of a string is capitalized.
5+
* @param {String} word String to check
6+
* @returns {Boolean} True if first letter is capitalized.
7+
*/
8+
function isFirstLetterCapitalized(word) {
9+
if (!word) {
10+
return false;
11+
}
12+
const firstLetter = word.charAt(0);
13+
return firstLetter.toUpperCase() === firstLetter;
14+
}
15+
16+
module.exports = isFirstLetterCapitalized;

lib/util/propTypes.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const variableUtil = require('./variable');
1212
const versionUtil = require('./version');
1313
const propWrapperUtil = require('./propWrapper');
1414
const astUtil = require('./ast');
15+
const isFirstLetterCapitalized = require('./isFirstLetterCapitalized');
1516

1617
/**
1718
* Check if node is function type.
@@ -83,6 +84,13 @@ function isInsideClassBody(node) {
8384
return false;
8485
}
8586

87+
function startWithCapitalizedLetter(node) {
88+
return (
89+
node.parent.type === 'VariableDeclarator'
90+
&& !isFirstLetterCapitalized(node.parent.id.name)
91+
);
92+
}
93+
8694
module.exports = function propTypesInstructions(context, components, utils) {
8795
// Used to track the type annotations in scope.
8896
// Necessary because babel's scopes do not track type annotations.
@@ -878,7 +886,7 @@ module.exports = function propTypesInstructions(context, components, utils) {
878886
}
879887

880888
// Should ignore function that not return JSXElement
881-
if (!utils.isReturningJSXOrNull(node)) {
889+
if (!utils.isReturningJSXOrNull(node) || startWithCapitalizedLetter(node)) {
882890
return;
883891
}
884892

tests/lib/rules/no-unused-prop-types.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3689,6 +3689,80 @@ ruleTester.run('no-unused-prop-types', rule, {
36893689
}
36903690
`,
36913691
parser: parsers.BABEL_ESLINT
3692+
},
3693+
{
3694+
code: `
3695+
const Home = () => {
3696+
const renderStaticList = ({
3697+
item,
3698+
}: {
3699+
item: IContent;
3700+
}) => (
3701+
<Section
3702+
icon={<FlashImage />}
3703+
title={item.title}
3704+
titleFontSize={theme.typography.FONT_SIZE_24}
3705+
>
3706+
<StaticFlatList
3707+
data={item.pointsOfSale}
3708+
renderItem={renderStaticItem}
3709+
keyExtractor={staticItemKeyExtractor}
3710+
/>
3711+
</Section>
3712+
);
3713+
3714+
return (
3715+
<SafeAreaViewWrapper>
3716+
<LightStatusBar />
3717+
<HomeFlatList
3718+
ListHeaderComponent={listHeaderComponent}
3719+
data={home?.static}
3720+
renderItem={renderStaticList}
3721+
keyExtractor={staticListKeyExtractor}
3722+
/>
3723+
</SafeAreaViewWrapper>
3724+
);
3725+
};
3726+
`,
3727+
parser: parsers['@TYPESCRIPT_ESLINT']
3728+
},
3729+
{
3730+
code: `
3731+
const Home = () => {
3732+
const renderStaticList = function({
3733+
item,
3734+
}: {
3735+
item: IContent;
3736+
}) {
3737+
return (
3738+
<Section
3739+
icon={<FlashImage />}
3740+
title={item.title}
3741+
titleFontSize={theme.typography.FONT_SIZE_24}
3742+
>
3743+
<StaticFlatList
3744+
data={item.pointsOfSale}
3745+
renderItem={renderStaticItem}
3746+
keyExtractor={staticItemKeyExtractor}
3747+
/>
3748+
</Section>
3749+
)
3750+
};
3751+
3752+
return (
3753+
<SafeAreaViewWrapper>
3754+
<LightStatusBar />
3755+
<HomeFlatList
3756+
ListHeaderComponent={listHeaderComponent}
3757+
data={home?.static}
3758+
renderItem={renderStaticList}
3759+
keyExtractor={staticListKeyExtractor}
3760+
/>
3761+
</SafeAreaViewWrapper>
3762+
);
3763+
};
3764+
`,
3765+
parser: parsers['@TYPESCRIPT_ESLINT']
36923766
}
36933767
])
36943768
),
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
'use strict';
2+
3+
const assert = require('assert');
4+
5+
const isFirstLetterCapitalized = require('../../lib/util/isFirstLetterCapitalized');
6+
7+
describe('isFirstLetterCapitalized', () => {
8+
it('should return false for invalid input', () => {
9+
assert.equal(isFirstLetterCapitalized(), false);
10+
assert.equal(isFirstLetterCapitalized(null), false);
11+
assert.equal(isFirstLetterCapitalized(''), false);
12+
});
13+
14+
it('should return false for uncapitalized string', () => {
15+
assert.equal(isFirstLetterCapitalized('isCapitalized'), false);
16+
assert.equal(isFirstLetterCapitalized('lowercase'), false);
17+
});
18+
19+
it('should return true for capitalized string', () => {
20+
assert.equal(isFirstLetterCapitalized('IsCapitalized'), true);
21+
assert.equal(isFirstLetterCapitalized('UPPERCASE'), true);
22+
});
23+
});

0 commit comments

Comments
 (0)