Skip to content

Commit

Permalink
React.ElementProps<typeof C>, #147
Browse files Browse the repository at this point in the history
  • Loading branch information
brigand committed Dec 1, 2017
1 parent e4a818f commit d9ceec4
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 4 deletions.
@@ -0,0 +1,15 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`react-dot-elementprops-normal 1`] = `
"'use strict';
var React = require('react');
var MenuItem = require('./MenuItem');
var C = function C(props) {
return React.createElement('div', null);
};
C.propTypes = {
foo: require('prop-types').shape(MenuItem.propTypes).isRequired
};"
`;
19 changes: 19 additions & 0 deletions src/__tests__/react-dot-elementprops-normal-test.js
@@ -0,0 +1,19 @@
const babel = require('babel-core');
const content = `
const React = require('react');
const MenuItem = require('./MenuItem');
type Props = {
foo: React.ElementProps<typeof MenuItem>,
};
const C = (props: Props) => <div />;
`;

it('react-dot-elementprops-normal', () => {
const res = babel.transform(content, {
babelrc: false,
presets: ['es2015', 'stage-1', 'react'],
plugins: ['syntax-flow', require('../')],
}).code;
expect(res).toMatchSnapshot();
});
34 changes: 34 additions & 0 deletions src/convertToPropTypes.js
Expand Up @@ -106,6 +106,40 @@ export default function convertToPropTypes(node, importedTypes, internalTypes) {
resultPropType = {'type': 'shape-intersect-runtime', properties: propTypes};
}
}
// https://github.com/brigand/babel-plugin-flow-react-proptypes/issues/147
else if (node.type === 'GenericTypeAnnotation'
&& node.id.type === 'QualifiedTypeIdentifier'
&& node.id.qualification
&& node.id.qualification.name === 'React'
&& node.id.id
&& node.id.id.name === 'ElementProps'
) {
const tp = node.typeParameters && node.typeParameters.params;
if (!tp || tp.length !== 1) {
throw new TypeError(`babel-plugin-flow-react-proptypes expected React.ElementProps to have one type parameter`);
}
if (tp[0].type === 'StringLiteralTypeAnnotation') {
resultPropType = {
type: 'object',
properties: [],
};
}
else if (tp[0].type === 'TypeofTypeAnnotation') {
const { argument } = tp[0];
const { id } = argument;
if (id.type !== 'Identifier') {
throw new TypeError(`babel-plugin-flow-react-proptypes expected React.ElementProps<typeof OneIdentifier>, but found some other type parameter`);
}
const { name } = id;
resultPropType = {
type: 'reference',
propertyPath: [name, 'propTypes'],
};
}
else {
throw new TypeError(`babel-plugin-flow-react-proptypes expected React.ElementProps to either be e.g. React.ElementProps<'div'> or React.ElementProps<typeof SomeComponent> `);
}
}
// Exact
else if (node.type === 'GenericTypeAnnotation' && node.id.name === '$Exact') {
resultPropType = {
Expand Down
25 changes: 21 additions & 4 deletions src/makePropTypesAst.js
Expand Up @@ -48,17 +48,20 @@ const anyTemplate = template(`
* @returns {*} AST expression always returning an object.
*/
export function makePropTypesAstForPropTypesAssignment(propTypeData) {
let node = null;
if (propTypeData.type === 'shape-intersect-runtime') {
// For top-level usage, e.g. Foo.proptype, return
// an expression returning an object.
return makeObjectMergeAstForShapeIntersectRuntime(propTypeData);
node = makeObjectMergeAstForShapeIntersectRuntime(propTypeData);
}
else if (propTypeData.type === 'shape') {
return makeObjectAstForShape(propTypeData);
node = makeObjectAstForShape(propTypeData);
}
else if (propTypeData.type === 'raw') {
return makeObjectAstForRaw(propTypeData);
node = makeObjectAstForRaw(propTypeData);
}

return node;
};


Expand Down Expand Up @@ -275,7 +278,7 @@ function makePropType(data, isExact) {
}
else if (method === 'any') {
markFullExpressionAsRequired = false;

if (data.isRequired) {
node = anyTemplate().expression;
}
Expand Down Expand Up @@ -356,6 +359,20 @@ function makePropType(data, isExact) {
[t.arrayExpression(data.options.map(item => makePropType(item)))]
);
}
else if (method === 'reference') {
const pp = data.propertyPath.slice();
let valueNode = t.identifier(pp.shift());
while (pp.length) {
valueNode = t.memberExpression(valueNode, t.identifier(pp.shift()));
}
node = t.callExpression(
t.memberExpression(
node,
t.identifier('shape'),
),
[valueNode],
);
}
else if (method === 'void') {
markFullExpressionAsRequired = false;
node = dontSetTemplate().expression;
Expand Down

0 comments on commit d9ceec4

Please sign in to comment.