Skip to content

Commit

Permalink
[Fix] prop-types/no-unused-prop-types: handle CallExpression in R…
Browse files Browse the repository at this point in the history
…eturnType

Fixes #2795.
  • Loading branch information
hank121314 authored and ljharb committed Sep 23, 2020
1 parent 4da7451 commit 22cc878
Show file tree
Hide file tree
Showing 5 changed files with 288 additions and 72 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -22,10 +22,12 @@ This change log adheres to standards from [Keep a CHANGELOG](http://keepachangel
* [`prop-types`]: Detect JSX returned by sequential expression ([#2801][] @mikol)
* [`jsx-props-no-multi-spaces`]: "Expected no line gap between" false positive ([#2792][] @karolina-benitez)
* [`no-unknown-property`]: check attributes with any input case ([#2790][] @julienw)
* [`prop-types`]/[`no-unused-prop-types`]: handle CallExpression in ReturnType ([#2802][] @hank121314)

### Changed
* [Tests] [`jsx-one-expression-per-line`]: add passing tests ([#2799][] @TaLeaMonet)

[#2802]: https://github.com/yannickcr/eslint-plugin-react/pull/2802
[#2801]: https://github.com/yannickcr/eslint-plugin-react/pull/2801
[#2799]: https://github.com/yannickcr/eslint-plugin-react/pull/2799
[#2796]: https://github.com/yannickcr/eslint-plugin-react/pull/2796
Expand Down
9 changes: 8 additions & 1 deletion lib/util/ast.js
Expand Up @@ -261,6 +261,12 @@ function isTSTypeQuery(node) {
return nodeType === 'TSTypeQuery';
}

function isTSTypeParameterInstantiation(node) {
if (!node) return false;
const nodeType = node.type;
return nodeType === 'TSTypeParameterInstantiation';
}

module.exports = {
findReturnStatement,
getFirstNodeInLine,
Expand All @@ -283,5 +289,6 @@ module.exports = {
isTSTypeAliasDeclaration,
isTSParenthesizedType,
isTSFunctionType,
isTSTypeQuery
isTSTypeQuery,
isTSTypeParameterInstantiation
};
54 changes: 32 additions & 22 deletions lib/util/propTypes.js
Expand Up @@ -502,27 +502,20 @@ module.exports = function propTypesInstructions(context, components, utils) {
this.referenceNameMap = new Set();
this.sourceCode = context.getSourceCode();
this.shouldIgnorePropTypes = false;
this.startWithTSTypeAnnotation();
this.visitTSNode(this.propTypes);
this.endAndStructDeclaredPropTypes();
}

startWithTSTypeAnnotation() {
if (astUtil.isTSTypeAnnotation(this.propTypes)) {
const typeAnnotation = this.propTypes.typeAnnotation;
this.visitTSNode(typeAnnotation);
} else {
// weird cases such as TSTypeFunction
this.shouldIgnorePropTypes = true;
}
}

/**
* The node will be distribute to different function.
* @param {ASTNode} node
*/
visitTSNode(node) {
if (!node) return;
if (astUtil.isTSTypeReference(node)) {
if (astUtil.isTSTypeAnnotation(node)) {
const typeAnnotation = node.typeAnnotation;
this.visitTSNode(typeAnnotation);
} else if (astUtil.isTSTypeReference(node)) {
this.searchDeclarationByName(node);
} else if (astUtil.isTSInterfaceHeritage(node)) {
this.searchDeclarationByName(node);
Expand All @@ -535,12 +528,10 @@ module.exports = function propTypesInstructions(context, components, utils) {
this.convertIntersectionTypeToPropTypes(node);
} else if (astUtil.isTSParenthesizedType(node)) {
const typeAnnotation = node.typeAnnotation;
if (astUtil.isTSTypeLiteral(typeAnnotation)) {
// Check node is an object literal
if (Array.isArray(node.typeAnnotation.members)) {
this.foundDeclaredPropertiesList = this.foundDeclaredPropertiesList
.concat(node.typeAnnotation.members);
}
this.visitTSNode(typeAnnotation);
} else if (astUtil.isTSTypeParameterInstantiation(node)) {
if (Array.isArray(node.params)) {
node.params.forEach(this.visitTSNode, this);
}
} else {
this.shouldIgnorePropTypes = true;
Expand Down Expand Up @@ -671,6 +662,19 @@ module.exports = function propTypesInstructions(context, components, utils) {
switch (res.type) {
case 'ObjectExpression':
iterateProperties(context, res.properties, (key, value, propNode) => {
if (propNode && propNode.argument && propNode.argument.type === 'CallExpression') {
if (propNode.argument.typeParameters) {
this.visitTSNode(propNode.argument.typeParameters);
} else {
// Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types.
this.shouldIgnorePropTypes = true;
return;
}
}
if (!value) {
this.shouldIgnorePropTypes = true;
return;
}
const types = buildReactDeclarationTypes(value, key);
types.fullName = key;
types.name = key;
Expand All @@ -679,6 +683,14 @@ module.exports = function propTypesInstructions(context, components, utils) {
this.declaredPropTypes[key] = types;
});
break;
case 'CallExpression':
if (res.typeParameters) {
this.visitTSNode(res.typeParameters);
} else {
// Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types.
this.shouldIgnorePropTypes = true;
}
break;
default:
}
}
Expand All @@ -689,15 +701,13 @@ module.exports = function propTypesInstructions(context, components, utils) {
// Handle ReturnType<()=>returnType>
if (astUtil.isTSFunctionType(returnType)) {
if (astUtil.isTSTypeAnnotation(returnType.returnType)) {
const returnTypeAnnotation = returnType.returnType.typeAnnotation;
this.visitTSNode(returnTypeAnnotation);
this.visitTSNode(returnType.returnType);
return;
}
// This line is trying to handle typescript-eslint-parser
// typescript-eslint-parser TSFunction name returnType as typeAnnotation
if (astUtil.isTSTypeAnnotation(returnType.typeAnnotation)) {
const returnTypeAnnotation = returnType.typeAnnotation.typeAnnotation;
this.visitTSNode(returnTypeAnnotation);
this.visitTSNode(returnType.typeAnnotation);
return;
}
}
Expand Down

0 comments on commit 22cc878

Please sign in to comment.