diff --git a/lib/rules/default-props-match-prop-types.js b/lib/rules/default-props-match-prop-types.js index 2fe1351306..c9a31937e6 100644 --- a/lib/rules/default-props-match-prop-types.js +++ b/lib/rules/default-props-match-prop-types.js @@ -163,23 +163,46 @@ module.exports = { })); } + /** + * Handles Props defined in IntersectionTypeAnnotation nodes + * e.g. type Props = PropsA & PropsB + * @param {ASTNode} intersectionTypeAnnotation ObjectExpression node. + * @returns {Object[]} + */ + function getPropertiesFromIntersectionTypeAnnotationNode(annotation) { + return annotation.types.reduce((properties, type) => { + annotation = resolveGenericTypeAnnotation(type); + + if (annotation && annotation.id) { + annotation = findVariableByName(annotation.id.name); + } + + return properties.concat(annotation.properties); + }, []); + } + /** * Extracts a PropType from a TypeAnnotation node. * @param {ASTNode} node TypeAnnotation node. * @returns {Object[]} Array of PropType object representations, to be consumed by `addPropTypesToComponent`. */ function getPropTypesFromTypeAnnotation(node) { - let properties; + let properties = []; switch (node.typeAnnotation.type) { case 'GenericTypeAnnotation': let annotation = resolveGenericTypeAnnotation(node.typeAnnotation); - if (annotation && annotation.id) { - annotation = findVariableByName(annotation.id.name); + if (annotation && annotation.type === 'IntersectionTypeAnnotation') { + properties = getPropertiesFromIntersectionTypeAnnotationNode(annotation); + } else { + if (annotation && annotation.id) { + annotation = findVariableByName(annotation.id.name); + } + + properties = annotation ? (annotation.properties || []) : []; } - properties = annotation ? (annotation.properties || []) : []; break; case 'UnionTypeAnnotation': @@ -314,7 +337,6 @@ module.exports = { if (!component) { return; } - addPropTypesToComponent(component, getPropTypesFromTypeAnnotation(node.typeAnnotation, context)); } diff --git a/lib/rules/no-unused-prop-types.js b/lib/rules/no-unused-prop-types.js index f595ce58ee..d076368777 100644 --- a/lib/rules/no-unused-prop-types.js +++ b/lib/rules/no-unused-prop-types.js @@ -736,6 +736,26 @@ module.exports = { return; } break; + case 'IntersectionTypeAnnotation': + propTypes.types.forEach(annotation => { + const propsType = typeScope(annotation.id.name); + iterateProperties(propsType.properties, (key, value) => { + if (!value) { + ignorePropsValidation = true; + return; + } + + let types = buildTypeAnnotationDeclarationTypes(value, key); + if (types === true) { + types = {}; + } + types.fullName = key; + types.name = key; + types.node = value; + declaredPropTypes.push(types); + }); + }); + break; case null: break; default: diff --git a/lib/rules/prop-types.js b/lib/rules/prop-types.js index 13aa98e718..4a9d1d6c6f 100644 --- a/lib/rules/prop-types.js +++ b/lib/rules/prop-types.js @@ -12,7 +12,6 @@ const Components = require('../util/Components'); const variable = require('../util/variable'); const annotations = require('../util/annotations'); const versionUtil = require('../util/version'); - // ------------------------------------------------------------------------------ // Constants // ------------------------------------------------------------------------------ @@ -794,6 +793,18 @@ module.exports = { return; } break; + case 'IntersectionTypeAnnotation': + propTypes.types.forEach(annotation => { + const propsType = typeScope(annotation.id.name); + iterateProperties(propsType.properties, (key, value) => { + if (!value) { + ignorePropsValidation = true; + return; + } + declaredPropTypes[key] = buildTypeAnnotationDeclarationTypes(value); + }); + }); + break; case null: break; default: diff --git a/tests/lib/rules/default-props-match-prop-types.js b/tests/lib/rules/default-props-match-prop-types.js index ed216d935a..52a64fa157 100644 --- a/tests/lib/rules/default-props-match-prop-types.js +++ b/tests/lib/rules/default-props-match-prop-types.js @@ -682,6 +682,25 @@ ruleTester.run('default-props-match-prop-types', rule, { ].join('\n'), parser: 'babel-eslint' }, + { + code: ` + type PropsA = { foo?: string }; + type PropsB = { bar?: string, fooBar: string }; + type Props = PropsA & PropsB; + + class Bar extends React.Component { + props: Props; + static defaultProps = { + foo: "foo", + } + + render() { + return