From 9a33fb4189cc2a2db0f86463fec9b112db936c46 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 9 Aug 2019 09:31:15 -0600 Subject: [PATCH] Allow self-referencing/recursive ts types (#2214) --- .../babel/proptypes-from-ts-props/index.js | 17 +++++- .../proptypes-from-ts-props/index.test.js | 58 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/scripts/babel/proptypes-from-ts-props/index.js b/scripts/babel/proptypes-from-ts-props/index.js index 5c2eef7339e..eaf2d1d53f3 100644 --- a/scripts/babel/proptypes-from-ts-props/index.js +++ b/scripts/babel/proptypes-from-ts-props/index.js @@ -122,6 +122,7 @@ function resolveArrayTypeToPropTypes(node, state) { function resolveIdentifierToPropTypes(node, state) { const typeDefinitions = state.get('typeDefinitions'); const types = state.get('types'); + const inflightResolves = state.get('inflightResolves') || new Set(); let identifier; switch (node.type) { @@ -223,7 +224,21 @@ function resolveIdentifierToPropTypes(node, state) { const identifierDefinition = typeDefinitions[identifier.name]; if (identifierDefinition) { - return getPropTypesForNode(identifierDefinition, true, state); + if (inflightResolves.has(identifier.name)) { + return types.memberExpression( + types.identifier('PropTypes'), + types.identifier('any') + ); + } + inflightResolves.add(identifier.name); + state.set('inflightResolves', inflightResolves); + + const propType = getPropTypesForNode(identifierDefinition, true, state); + + inflightResolves.delete(identifier.name); + state.set('inflightResolves', inflightResolves); + + return propType; } else { return null; } diff --git a/scripts/babel/proptypes-from-ts-props/index.test.js b/scripts/babel/proptypes-from-ts-props/index.test.js index ec4174f790d..7edfa6061d8 100644 --- a/scripts/babel/proptypes-from-ts-props/index.test.js +++ b/scripts/babel/proptypes-from-ts-props/index.test.js @@ -2361,6 +2361,64 @@ FooComponent.propTypes = { }); + describe('self-referencing types', () => { + + it(`doesn't explode on self-referencing interfaces`, () => { + const result = transform( + ` +import React, { SFC } from 'react'; +interface FooProps { + label: string; + children?: FooProps[]; +} +const FooComponent: SFC = () => { + return (
Hello World
); +}`, + babelOptions + ); + + expect(result.code).toBe(`import React from 'react'; +import PropTypes from "prop-types"; + +const FooComponent = () => { + return
Hello World
; +}; + +FooComponent.propTypes = { + label: PropTypes.string.isRequired, + children: PropTypes.arrayOf(PropTypes.any.isRequired) +};`); + }); + + it(`doesn't explode on self-referencing types`, () => { + const result = transform( + ` +import React, { SFC } from 'react'; +type FooProps = { + label: string; + children?: FooProps[]; +} +const FooComponent: SFC = () => { + return (
Hello World
); +}`, + babelOptions + ); + + expect(result.code).toBe(`import React from 'react'; +import PropTypes from "prop-types"; + +const FooComponent = () => { + return
Hello World
; +}; + +FooComponent.propTypes = { + label: PropTypes.string.isRequired, + children: PropTypes.arrayOf(PropTypes.any.isRequired) +};`); + }); + + }); + }); describe('remove types from exports', () => {