Skip to content

Commit 3a114e5

Browse files
committed
Support recursive type annotations, fixes jsx-eslint#913
1 parent a70cbce commit 3a114e5

File tree

2 files changed

+29
-5
lines changed

2 files changed

+29
-5
lines changed

lib/rules/prop-types.js

+16-5
Original file line numberDiff line numberDiff line change
@@ -484,14 +484,25 @@ module.exports = {
484484
* Creates the representation of the React props type annotation for the component.
485485
* The representation is used to verify nested used properties.
486486
* @param {ASTNode} annotation Type annotation for the props class property.
487+
* @param {Array} seen Keeps track of annotations we've already seen.
487488
* @return {Object|Boolean} The representation of the declaration, true means
488489
* the property is declared without the need for further analysis.
489490
*/
490-
function buildTypeAnnotationDeclarationTypes(annotation) {
491+
function buildTypeAnnotationDeclarationTypes(annotation, seen) {
492+
if (seen === void 0) {
493+
// Keeps track of annotations we've already seen to
494+
// prevent problems with cyclic types.
495+
seen = [];
496+
}
497+
if (seen.indexOf(annotation) > -1) {
498+
// this must be a recursive type annotation, just accept anything.
499+
return true;
500+
}
501+
seen.push(annotation);
491502
switch (annotation.type) {
492503
case 'GenericTypeAnnotation':
493504
if (typeScope(annotation.id.name)) {
494-
return buildTypeAnnotationDeclarationTypes(typeScope(annotation.id.name));
505+
return buildTypeAnnotationDeclarationTypes(typeScope(annotation.id.name), seen);
495506
}
496507
return true;
497508
case 'ObjectTypeAnnotation':
@@ -500,7 +511,7 @@ module.exports = {
500511
children: {}
501512
};
502513
iterateProperties(annotation.properties, function(childKey, childValue) {
503-
shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue);
514+
shapeTypeDefinition.children[childKey] = buildTypeAnnotationDeclarationTypes(childValue, seen);
504515
});
505516
return shapeTypeDefinition;
506517
case 'UnionTypeAnnotation':
@@ -509,7 +520,7 @@ module.exports = {
509520
children: []
510521
};
511522
for (var i = 0, j = annotation.types.length; i < j; i++) {
512-
var type = buildTypeAnnotationDeclarationTypes(annotation.types[i]);
523+
var type = buildTypeAnnotationDeclarationTypes(annotation.types[i], seen);
513524
// keep only complex type
514525
if (type !== true) {
515526
if (type.children === true) {
@@ -530,7 +541,7 @@ module.exports = {
530541
return {
531542
type: 'object',
532543
children: {
533-
__ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType)
544+
__ANY_KEY__: buildTypeAnnotationDeclarationTypes(annotation.elementType, seen)
534545
}
535546
};
536547
default:

tests/lib/rules/prop-types.js

+13
Original file line numberDiff line numberDiff line change
@@ -936,6 +936,19 @@ ruleTester.run('prop-types', rule, {
936936
'}'
937937
].join('\n'),
938938
parser: 'babel-eslint'
939+
}, {
940+
code: [
941+
'type Note = {text: string, children?: Note[]};',
942+
'type Props = {',
943+
' notes: Note[];',
944+
'};',
945+
'class Hello extends React.Component<void, Props, void> {',
946+
' render () {',
947+
' return <div>Hello {this.props.notes[0].text}</div>;',
948+
' }',
949+
'}'
950+
].join('\n'),
951+
parser: 'babel-eslint'
939952
}, {
940953
code: [
941954
'type Props = {name: Object;};',

0 commit comments

Comments
 (0)