diff --git a/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsFromPropTypes/index.ts b/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsFromPropTypes/index.ts index 2ca3db6b1..643f61f73 100644 --- a/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsFromPropTypes/index.ts +++ b/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsFromPropTypes/index.ts @@ -39,7 +39,7 @@ const visitReactComponentNode = ( request: FileMutationsRequest, ): Mutation | undefined => { // If the node is a class declaration, don't bother with prop types if it already declares a React.Component template - if (ts.isClassDeclaration(node)) { + if (ts.isClassLike(node)) { const extendsType = getClassExtendsType(node); if ( @@ -48,6 +48,12 @@ const visitReactComponentNode = ( ) { return undefined; } + } else if ( + node.parameters.at(0)?.getChildCount() && + node.parameters[0].getChildCount() > 1 + ) { + // if function already has type annotation, skip it + return undefined; } // Try to find a static `propTypes` member to indicate the interface diff --git a/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsMissing.ts b/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsMissing.ts index bd37ed871..3f683d2b8 100644 --- a/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsMissing.ts +++ b/src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsMissing.ts @@ -29,6 +29,15 @@ const visitReactComponentNode = ( node: ReactComponentNode, request: FileMutationsRequest, ) => { + if ( + !ts.isClassLike(node) && + node.parameters.at(0)?.getChildCount() && + node.parameters[0].getChildCount() > 1 + ) { + // if function already has type annotation, skip it + return undefined; + } + // Make sure a node doesn't yet exist to declare the node's props type const propsNode = getComponentPropsNode(request, node); if (propsNode !== undefined) { diff --git a/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/expected.tsx b/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/expected.tsx index 8bc657c8f..e6d668a9b 100644 --- a/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/expected.tsx +++ b/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/expected.tsx @@ -133,4 +133,58 @@ interface LaterAssignedComponentProps { string: PropTypes.string, stringRequired: PropTypes.string.isRequired, }; + +interface GreetingProps { + name?: string; +} + + + class Greeting extends React.Component { + render() { + return

Hello, {this.props.name}

; + } + } + + Greeting.propTypes = { + name: PropTypes.string, + }; + +interface HelloWorldComponentProps { + name?: string; +} + + + function HelloWorldComponent({ name }: HelloWorldComponentProps) { + return
Hello, {name}
; + } + + HelloWorldComponent.propTypes = { + name: PropTypes.string, + }; + +interface HeadingProps { + text?: string; +} + + + function Heading({ text }: HeadingProps) { + return

{text}

; + } + Heading.propTypes = { + text: PropTypes.string, + }; + Heading.defaultProps = { + text: "Hello, world!", + }; + + const LegendImage = function (props: any) { + return ( + (e.currentTarget.style.display = "block")} + onError={(e) => (e.currentTarget.style.display = "none")} + /> + ); + }; })(); diff --git a/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/original.tsx b/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/original.tsx index f65c325b3..815f4edb8 100644 --- a/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/original.tsx +++ b/test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/original.tsx @@ -75,4 +75,43 @@ import PropTypes from "prop-types"; string: PropTypes.string, stringRequired: PropTypes.string.isRequired, }; + + class Greeting extends React.Component { + render() { + return

Hello, {this.props.name}

; + } + } + + Greeting.propTypes = { + name: PropTypes.string, + }; + + function HelloWorldComponent({ name }) { + return
Hello, {name}
; + } + + HelloWorldComponent.propTypes = { + name: PropTypes.string, + }; + + function Heading({ text }) { + return

{text}

; + } + Heading.propTypes = { + text: PropTypes.string, + }; + Heading.defaultProps = { + text: "Hello, world!", + }; + + const LegendImage = function (props: any) { + return ( + (e.currentTarget.style.display = "block")} + onError={(e) => (e.currentTarget.style.display = "none")} + /> + ); + }; })();