From 614d3bd49773c6692018adea10750719b2fae4ab Mon Sep 17 00:00:00 2001 From: Jeremy Tice Date: Mon, 22 Jan 2018 15:45:41 -0500 Subject: [PATCH 1/3] Support recursive type annotations, fixes #913 Originally written by @phpnode as part of #1138 --- lib/rules/prop-types.js | 21 ++++++++++++++++----- tests/lib/rules/prop-types.js | 14 ++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index a17d7cd0e9..f25e137f69 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -466,11 +466,22 @@ module.exports = { * @return {Object} The representation of the declaration, empty object means * the property is declared without the need for further analysis. */ - function buildTypeAnnotationDeclarationTypes(annotation) { + function buildTypeAnnotationDeclarationTypes(annotation, seen) { + if (seen === void 0) { + // Keeps track of annotations we've already seen to + // prevent problems with recursive types. + seen = new Set(); + } + if (seen.has(annotation)) { + // This must be a recursive type annotation, so just accept anything. + return true; + } + seen.add(annotation); + switch (annotation.type) { case 'GenericTypeAnnotation': if (typeScope(annotation.id.name)) { - return buildTypeAnnotationDeclarationTypes(typeScope(annotation.id.name)); + return buildTypeAnnotationDeclarationTypes(typeScope(annotation.id.name), seen); } return {}; case 'ObjectTypeAnnotation': @@ -483,7 +494,7 @@ module.exports = { if (!childKey && !childValue) { containsObjectTypeSpread = true; } else { - shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue); + shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue, seen); } }); @@ -498,7 +509,7 @@ module.exports = { children: [] }; for (let i = 0, j = annotation.types.length; i < j; i++) { - const type = buildTypeAnnotationDeclarationTypes(annotation.types[i]); + const type = buildTypeAnnotationDeclarationTypes(annotation.types[i], seen); // keep only complex type if (Object.keys(type).length > 0) { if (type.children === true) { @@ -519,7 +530,7 @@ module.exports = { return { type: 'object', children: { - __ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType) + __ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType, seen) } }; default: diff --git a/tests/lib/rules/prop-types.js b/tests/lib/rules/prop-types.js index 3ee4072ac3..de25474b76 100644 --- a/tests/lib/rules/prop-types.js +++ b/tests/lib/rules/prop-types.js @@ -1581,6 +1581,20 @@ ruleTester.run('prop-types', rule, { ].join('\n'), settings: {react: {flowVersion: '0.52'}}, parser: 'babel-eslint' + }, { + code: [ + 'type Note = {text: string, children?: Note[]};', + 'type Props = {', + ' notes: Note[];', + '};', + 'class Hello extends React.Component {', + ' render () {', + ' return
Hello {this.props.notes[0].text}
;', + ' }', + '}' + ].join('\n'), + settings: {react: {flowVersion: '0.52'}}, + parser: 'babel-eslint' }, { code: [ 'import type Props from "fake";', From e4f7e3021adf8f7919a118bd4893f5ba866b1a28 Mon Sep 17 00:00:00 2001 From: Jeremy Tice Date: Tue, 23 Jan 2018 11:01:03 -0500 Subject: [PATCH 2/3] Mark all recursive proptypes as acceptable with {}, not true --- lib/rules/prop-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index f25e137f69..3938a3df3c 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -474,7 +474,7 @@ module.exports = { } if (seen.has(annotation)) { // This must be a recursive type annotation, so just accept anything. - return true; + return {}; } seen.add(annotation); From 509f2cbc211d1c31536d7d778b6b6d162b7c86bc Mon Sep 17 00:00:00 2001 From: Jeremy Tice Date: Fri, 26 Jan 2018 09:39:49 -0500 Subject: [PATCH 3/3] Use typeof === 'undefined' instead of void 0 --- lib/rules/prop-types.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 3938a3df3c..7466c3a2f1 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -467,7 +467,7 @@ module.exports = { * the property is declared without the need for further analysis. */ function buildTypeAnnotationDeclarationTypes(annotation, seen) { - if (seen === void 0) { + if (typeof seen === 'undefined') { // Keeps track of annotations we've already seen to // prevent problems with recursive types. seen = new Set();