Skip to content

Commit

Permalink
[Fix] no-unused-prop-types: handle optional chaining
Browse files Browse the repository at this point in the history
  • Loading branch information
hank121314 authored and ljharb committed Jun 29, 2020
1 parent c65b79e commit 85e40aa
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 3 deletions.
3 changes: 2 additions & 1 deletion lib/util/usedPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils)
let allNames;
let properties;
switch (node.type) {
case 'OptionalMemberExpression':
case 'MemberExpression':
name = getPropertyName(node);
if (name) {
Expand Down Expand Up @@ -521,7 +522,7 @@ module.exports = function usedPropTypesInstructions(context, components, utils)
});
},

MemberExpression(node) {
'MemberExpression, OptionalMemberExpression'(node) {
if (isPropTypesUsageByMemberExpression(node, context, utils, checkAsyncSafeLifeCycles)) {
markPropTypesAsUsed(node);
return;
Expand Down
132 changes: 130 additions & 2 deletions tests/lib/rules/no-unused-prop-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,18 @@ ruleTester.run('no-unused-prop-types', rule, {
'}'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'class Hello extends React.Component {',
' static propTypes = {',
' name: PropTypes.string',
' };',
' render() {',
' return <div>Hello {this.props?.name}</div>;',
' }',
'}'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'class Hello extends React.Component {',
Expand Down Expand Up @@ -698,6 +710,19 @@ ruleTester.run('no-unused-prop-types', rule, {
'}'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'class Hello extends React.Component {',
' constructor(props, context) {',
' super(props, context)',
' this.state = { status: props?.source?.uri }',
' }',
' static propTypes = {',
' source: PropTypes.object',
' };',
'}'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'class Hello extends React.Component {',
Expand All @@ -721,6 +746,16 @@ ruleTester.run('no-unused-prop-types', rule, {
'};'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
// Should not be detected as a component
code: [
'HelloJohn.prototype.render = function() {',
' return React.createElement(Hello, {',
' name: this.props?.firstname',
' });',
'};'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'function HelloComponent() {',
Expand All @@ -735,6 +770,20 @@ ruleTester.run('no-unused-prop-types', rule, {
'module.exports = HelloComponent();'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'function HelloComponent() {',
' class Hello extends React.Component {',
' render() {',
' return <div>Hello {this.props?.name}</div>;',
' }',
' }',
' Hello.propTypes = { name: PropTypes.string };',
' return Hello;',
'}',
'module.exports = HelloComponent();'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'function HelloComponent() {',
Expand All @@ -749,6 +798,20 @@ ruleTester.run('no-unused-prop-types', rule, {
'module.exports = HelloComponent();'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'function HelloComponent() {',
' var Hello = createReactClass({',
' propTypes: { name: PropTypes.string },',
' render: function() {',
' return <div>Hello {this.props?.name}</div>;',
' }',
' });',
' return Hello;',
'}',
'module.exports = HelloComponent();'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'class DynamicHello extends Component {',
Expand Down Expand Up @@ -786,6 +849,20 @@ ruleTester.run('no-unused-prop-types', rule, {
'};'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'const Hello = (props) => {',
' let team = props?.names.map((name) => {',
' return <li>{name}, {props?.company}</li>;',
' });',
' return <ul>{team}</ul>;',
'};',
'Hello.propTypes = {',
' names: PropTypes.array,',
' company: PropTypes.string',
'};'
].join('\n'),
parser: parsers.BABEL_ESLINT
}, {
code: [
'export default {',
Expand Down Expand Up @@ -3247,6 +3324,15 @@ ruleTester.run('no-unused-prop-types', rule, {
'}'
].join('\n'),
parser: parsers.TYPESCRIPT_ESLINT
},
{
// neither TSTypeReference or TSTypeLiteral, we do nothing. Weird case
code: [
'const Hello = (props: () => any) => {',
' return <div>{props?.firstname}</div>;',
'}'
].join('\n'),
parser: parsers.TYPESCRIPT_ESLINT
}
],

Expand Down Expand Up @@ -5361,6 +5447,19 @@ ruleTester.run('no-unused-prop-types', rule, {
errors: [{
message: '\'lastname\' PropType is defined but prop is never used'
}]
}, {
code: [
'type Person = {',
' lastname: string',
'};',
'const Hello = (props: Person) => {',
' return <div>Hello {props?.firstname}</div>;',
'}'
].join('\n'),
parser: parsers.BABEL_ESLINT,
errors: [{
message: '\'lastname\' PropType is defined but prop is never used'
}]
}, {
code: [
'type Person = {',
Expand All @@ -5376,8 +5475,22 @@ ruleTester.run('no-unused-prop-types', rule, {
message: '\'lastname\' PropType is defined but prop is never used'
}
]
},
{
}, {
code: [
'type Person = {',
' lastname: string',
'};',
'const Hello = (props: Person) => {',
' return <div>Hello {props?.firstname}</div>;',
'}'
].join('\n'),
parser: parsers.TYPESCRIPT_ESLINT,
errors: [
{
message: '\'lastname\' PropType is defined but prop is never used'
}
]
}, {
code: [
'type Person = {',
' lastname?: string',
Expand All @@ -5392,6 +5505,21 @@ ruleTester.run('no-unused-prop-types', rule, {
message: '\'lastname\' PropType is defined but prop is never used'
}
]
}, {
code: [
'type Person = {',
' lastname?: string',
'};',
'const Hello = (props: Person) => {',
' return <div>Hello {props?.firstname}</div>;',
'}'
].join('\n'),
parser: parsers.TYPESCRIPT_ESLINT,
errors: [
{
message: '\'lastname\' PropType is defined but prop is never used'
}
]
},
{
code: [
Expand Down

0 comments on commit 85e40aa

Please sign in to comment.