From c57cc315e4033faed7a35620704c135963b4e09f Mon Sep 17 00:00:00 2001 From: Hank Chen Date: Mon, 6 Jul 2020 14:00:23 +0800 Subject: [PATCH] [Fix] `prop-types`: handle component returning null Fixes #2705. --- lib/util/propTypes.js | 23 +------------ tests/lib/rules/prop-types.js | 62 ++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 23 deletions(-) mode change 100755 => 100644 tests/lib/rules/prop-types.js diff --git a/lib/util/propTypes.js b/lib/util/propTypes.js index 6d0a19dab5..bf9103a905 100644 --- a/lib/util/propTypes.js +++ b/lib/util/propTypes.js @@ -12,8 +12,6 @@ const variableUtil = require('./variable'); const versionUtil = require('./version'); const propWrapperUtil = require('./propWrapper'); const getKeyValue = require('./ast').getKeyValue; -const findReturnStatement = require('./ast').findReturnStatement; -const isJSX = require('./jsx').isJSX; /** * Checks if we are declaring a props as a generic type in a flow-annotated class. @@ -72,25 +70,6 @@ function isInsideClassBody(node) { return false; } -/** - * Checks if a node is a Function and return JSXElement - * - * @param {ASTNode} node the AST node being checked. - * @returns {Boolean} True if the node is a Function and return JSXElement. - */ -function isJSXFunctionComponent(node) { - if (node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { - const res = findReturnStatement(node); - // If function return JSXElement with return keyword. - if (res) { - return isJSX(res.argument); - } - // If function return JSXElement without return keyword; - return isJSX(node.body); - } - return false; -} - module.exports = function propTypesInstructions(context, components, utils) { // Used to track the type annotations in scope. // Necessary because babel's scopes do not track type annotations. @@ -699,7 +678,7 @@ module.exports = function propTypesInstructions(context, components, utils) { } // Should ignore function that not return JSXElement - if (!isJSXFunctionComponent(node)) { + if (!utils.isReturningJSXOrNull(node)) { return; } diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js old mode 100755 new mode 100644 index bd0572a6b3..0089d1c812 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -2524,7 +2524,52 @@ ruleTester.run('prop-types', rule, { MyComponent.propTypes = { hello: PropTypes.string.isRequired, }; - ` + `, + { + code: ` + interface Props { + value?: string; + } + + // without the | null, all ok, with it, it is broken + function Test ({ value }: Props): React.ReactElement | null { + if (!value) { + return null; + } + + return
{value}
; + }`, + parser: parsers.TYPESCRIPT_ESLINT + }, + { + code: ` + interface Props { + value?: string; + } + + // without the | null, all ok, with it, it is broken + function Test ({ value }: Props): React.ReactElement | null { + if (!value) { + return
{value}
;; + } + + return null; + }`, + parser: parsers.TYPESCRIPT_ESLINT + }, + { + code: ` + interface Props { + value?: string; + } + const Hello = (props: Props) => { + if (props.value) { + return
; + } + return null; + }`, + parser: parsers.TYPESCRIPT_ESLINT + } ], invalid: [ @@ -5101,6 +5146,21 @@ ruleTester.run('prop-types', rule, { errors: [{ message: '\'foo.baz\' is missing in props validation' }] + }, + { + code: ` + interface Props { + } + const Hello = (props: Props) => { + if (props.value) { + return
; + } + return null; + }`, + parser: parsers.TYPESCRIPT_ESLINT, + errors: [{ + message: '\'value\' is missing in props validation' + }] } ] });