Skip to content

Commit 00106e4

Browse files
author
Joachim Seminck
committed
Add IntersectionTypeAnnotation support to default-props-match-prop-types
1 parent c7cf2ae commit 00106e4

File tree

2 files changed

+76
-5
lines changed

2 files changed

+76
-5
lines changed

lib/rules/default-props-match-prop-types.js

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,23 +163,47 @@ module.exports = {
163163
}));
164164
}
165165

166+
/**
167+
* Handles Props defined in IntersectionTypeAnnotation nodes
168+
* e.g. type Props = PropsA & PropsB
169+
* @param {ASTNode} intersectionTypeAnnotation ObjectExpression node.
170+
* @returns {Object[]}
171+
*/
172+
function getPropertiesFromIntersectionTypeAnnotationNode(annotation) {
173+
return annotation.types.reduce((properties, type) => {
174+
annotation = resolveGenericTypeAnnotation(type);
175+
176+
if (annotation && annotation.id) {
177+
annotation = findVariableByName(annotation.id.name);
178+
}
179+
180+
return properties.concat(annotation.properties);
181+
}, []);
182+
}
183+
166184
/**
167185
* Extracts a PropType from a TypeAnnotation node.
168186
* @param {ASTNode} node TypeAnnotation node.
169187
* @returns {Object[]} Array of PropType object representations, to be consumed by `addPropTypesToComponent`.
170188
*/
171189
function getPropTypesFromTypeAnnotation(node) {
172-
let properties;
190+
let properties = [];
173191

174192
switch (node.typeAnnotation.type) {
175193
case 'GenericTypeAnnotation':
176194
let annotation = resolveGenericTypeAnnotation(node.typeAnnotation);
177195

178-
if (annotation && annotation.id) {
179-
annotation = findVariableByName(annotation.id.name);
196+
if (annotation && annotation.type === 'IntersectionTypeAnnotation') {
197+
properties = getPropertiesFromIntersectionTypeAnnotationNode(annotation);
198+
} else {
199+
if (annotation && annotation.id) {
200+
annotation = findVariableByName(annotation.id.name);
201+
}
202+
203+
properties = annotation ? (annotation.properties || []) : [];
180204
}
181205

182-
properties = annotation ? (annotation.properties || []) : [];
206+
183207
break;
184208

185209
case 'UnionTypeAnnotation':
@@ -314,7 +338,6 @@ module.exports = {
314338
if (!component) {
315339
return;
316340
}
317-
318341
addPropTypesToComponent(component, getPropTypesFromTypeAnnotation(node.typeAnnotation, context));
319342
}
320343

tests/lib/rules/default-props-match-prop-types.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -682,6 +682,26 @@ ruleTester.run('default-props-match-prop-types', rule, {
682682
].join('\n'),
683683
parser: 'babel-eslint'
684684
},
685+
{
686+
code: `
687+
type PropsA = { foo?: string };
688+
type PropsB = { bar?: string, fooBar: string };
689+
type Props = PropsA & PropsB;
690+
691+
class Bar extends React.Component {
692+
props: Props;
693+
static defaultProps = {
694+
foo: "foo",
695+
bar: "bar",
696+
}
697+
698+
render() {
699+
return <div>{this.props.foo} - {this.props.bar}</div>
700+
}
701+
}
702+
`,
703+
parser: 'babel-eslint'
704+
},
685705
{
686706
code: [
687707
'import type Props from "fake";',
@@ -1509,6 +1529,34 @@ ruleTester.run('default-props-match-prop-types', rule, {
15091529
column: 36
15101530
}
15111531
]
1532+
},
1533+
{
1534+
code: `
1535+
type PropsA = { foo: string };
1536+
type PropsB = { bar: string };
1537+
type Props = PropsA & PropsB;
1538+
1539+
class Bar extends React.Component {
1540+
props: Props;
1541+
static defaultProps = {
1542+
fooBar: "fooBar",
1543+
foo: "foo",
1544+
}
1545+
1546+
render() {
1547+
return <div>{this.props.foo} - {this.props.bar}</div>
1548+
}
1549+
}
1550+
`,
1551+
parser: 'babel-eslint',
1552+
errors: [
1553+
{
1554+
message: 'defaultProp "fooBar" has no corresponding propTypes declaration.'
1555+
},
1556+
{
1557+
message: 'defaultProp "foo" defined for isRequired propType.'
1558+
}
1559+
]
15121560
}
15131561
]
15141562
});

0 commit comments

Comments
 (0)