Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fix] prop-types: Should handle CallExpression in ReturnType #2802

Merged
merged 1 commit into from Sep 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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