Skip to content

Commit dc9f408

Browse files
fix: duplicate react props added (#2057)
## PR Checklist - [x] Addresses an existing open issue: fixes #2019 - [x] That issue was marked as [`status: accepting prs`](https://github.com/JoshuaKGoldberg/TypeStat/issues?q=is%3Aopen+is%3Aissue+label%3A%22status%3A+accepting+prs%22) - [x] Steps in [CONTRIBUTING.md](https://github.com/JoshuaKGoldberg/TypeStat/blob/main/.github/CONTRIBUTING.md) were taken ## Overview I think the checks are little bit late but I didn't find better place.
1 parent a5f7aea commit dc9f408

4 files changed

Lines changed: 109 additions & 1 deletion

File tree

src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsFromPropTypes/index.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const visitReactComponentNode = (
3939
request: FileMutationsRequest,
4040
): Mutation | undefined => {
4141
// If the node is a class declaration, don't bother with prop types if it already declares a React.Component template
42-
if (ts.isClassDeclaration(node)) {
42+
if (ts.isClassLike(node)) {
4343
const extendsType = getClassExtendsType(node);
4444

4545
if (
@@ -48,6 +48,12 @@ const visitReactComponentNode = (
4848
) {
4949
return undefined;
5050
}
51+
} else if (
52+
node.parameters.at(0)?.getChildCount() &&
53+
node.parameters[0].getChildCount() > 1
54+
) {
55+
// if function already has type annotation, skip it
56+
return undefined;
5157
}
5258

5359
// Try to find a static `propTypes` member to indicate the interface

src/mutators/builtIn/fixIncompleteTypes/fixIncompleteReactTypes/fixReactPropsMissing.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ const visitReactComponentNode = (
2929
node: ReactComponentNode,
3030
request: FileMutationsRequest,
3131
) => {
32+
if (
33+
!ts.isClassLike(node) &&
34+
node.parameters.at(0)?.getChildCount() &&
35+
node.parameters[0].getChildCount() > 1
36+
) {
37+
// if function already has type annotation, skip it
38+
return undefined;
39+
}
40+
3241
// Make sure a node doesn't yet exist to declare the node's props type
3342
const propsNode = getComponentPropsNode(request, node);
3443
if (propsNode !== undefined) {

test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/expected.tsx

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,58 @@ interface LaterAssignedComponentProps {
133133
string: PropTypes.string,
134134
stringRequired: PropTypes.string.isRequired,
135135
};
136+
137+
interface GreetingProps {
138+
name?: string;
139+
}
140+
141+
142+
class Greeting extends React.Component<GreetingProps> {
143+
render() {
144+
return <h1>Hello, {this.props.name}</h1>;
145+
}
146+
}
147+
148+
Greeting.propTypes = {
149+
name: PropTypes.string,
150+
};
151+
152+
interface HelloWorldComponentProps {
153+
name?: string;
154+
}
155+
156+
157+
function HelloWorldComponent({ name }: HelloWorldComponentProps) {
158+
return <div>Hello, {name}</div>;
159+
}
160+
161+
HelloWorldComponent.propTypes = {
162+
name: PropTypes.string,
163+
};
164+
165+
interface HeadingProps {
166+
text?: string;
167+
}
168+
169+
170+
function Heading({ text }: HeadingProps) {
171+
return <h1>{text}</h1>;
172+
}
173+
Heading.propTypes = {
174+
text: PropTypes.string,
175+
};
176+
Heading.defaultProps = {
177+
text: "Hello, world!",
178+
};
179+
180+
const LegendImage = function (props: any) {
181+
return (
182+
<img
183+
{...props}
184+
style={{ display: "none", marginTop: "4px" }}
185+
onLoad={(e) => (e.currentTarget.style.display = "block")}
186+
onError={(e) => (e.currentTarget.style.display = "none")}
187+
/>
188+
);
189+
};
136190
})();

test/cases/fixes/incompleteTypes/reactTypes/reactPropsFromPropTypes/all/original.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,4 +75,43 @@ import PropTypes from "prop-types";
7575
string: PropTypes.string,
7676
stringRequired: PropTypes.string.isRequired,
7777
};
78+
79+
class Greeting extends React.Component {
80+
render() {
81+
return <h1>Hello, {this.props.name}</h1>;
82+
}
83+
}
84+
85+
Greeting.propTypes = {
86+
name: PropTypes.string,
87+
};
88+
89+
function HelloWorldComponent({ name }) {
90+
return <div>Hello, {name}</div>;
91+
}
92+
93+
HelloWorldComponent.propTypes = {
94+
name: PropTypes.string,
95+
};
96+
97+
function Heading({ text }) {
98+
return <h1>{text}</h1>;
99+
}
100+
Heading.propTypes = {
101+
text: PropTypes.string,
102+
};
103+
Heading.defaultProps = {
104+
text: "Hello, world!",
105+
};
106+
107+
const LegendImage = function (props: any) {
108+
return (
109+
<img
110+
{...props}
111+
style={{ display: "none", marginTop: "4px" }}
112+
onLoad={(e) => (e.currentTarget.style.display = "block")}
113+
onError={(e) => (e.currentTarget.style.display = "none")}
114+
/>
115+
);
116+
};
78117
})();

0 commit comments

Comments
 (0)