Skip to content

Commit

Permalink
Correctly process literal enum values in TS->propType conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
chandlerprall committed Feb 27, 2019
1 parent 62bee3d commit 2a3f97a
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 2 deletions.
29 changes: 27 additions & 2 deletions scripts/babel/proptypes-from-ts-props/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,9 @@ function isPropTypeRequired(types, propType) {
}

function makePropTypeRequired(types, propType) {
// can't make literals required no matter how hard we try
if (types.isLiteral(propType) === true) return propType;

return types.memberExpression(
propType,
types.identifier('isRequired')
Expand Down Expand Up @@ -275,6 +278,22 @@ function areExpressionsIdentical(a, b) {
return aCode === bCode;
}

/**
* Converts any literal node (StringLiteral, etc) into a PropTypes.oneOF([ literalNode ])
* so it can be used in any proptype expression
*/
function convertLiteralToOneOf(types, literalNode) {
return types.callExpression(
types.memberExpression(
types.identifier('PropTypes'),
types.identifier('oneOf')
),
[
types.arrayExpression([ literalNode ])
]
);
}

/**
* Heavy lifter to generate the proptype AST for a node. Initially called by `processComponentDeclaration`,
* its return value is set as the component's `propTypes` value. This function calls itself recursively to translate
Expand Down Expand Up @@ -345,6 +364,12 @@ function getPropTypesForNode(node, optional, state) {
...((mergedProperties[typeProperty.key.name] ? mergedProperties[typeProperty.key.name].leadingComments : null) || []),
];

let propTypeValue = typeProperty.value;
if (types.isLiteral(propTypeValue)) {
// can't use a literal straight, wrap it with PropTypes.oneOf([ the_literal ])
propTypeValue = convertLiteralToOneOf(types, propTypeValue);
}

// if this property has already been found, the only action is to potentially change it to optional
if (mergedProperties.hasOwnProperty(typeProperty.key.name)) {
const existing = mergedProperties[typeProperty.key.name];
Expand All @@ -356,7 +381,7 @@ function getPropTypesForNode(node, optional, state) {
),
[
types.arrayExpression(
[existing, typeProperty.value]
[existing, propTypeValue]
)
]
);
Expand All @@ -367,7 +392,7 @@ function getPropTypesForNode(node, optional, state) {
}
} else {
// property hasn't been seen yet, add it
mergedProperties[typeProperty.key.name] = typeProperty.value;
mergedProperties[typeProperty.key.name] = propTypeValue;
}

mergedProperties[typeProperty.key.name].leadingComments = leadingComments;
Expand Down
25 changes: 25 additions & 0 deletions scripts/babel/proptypes-from-ts-props/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1006,6 +1006,31 @@ FooComponent.propTypes = {
};`);
});

it('intersects overlapping string enums in ExclusiveUnion', () => {
const result = transform(
`
import React from 'react';
interface IFooProps {type: 'foo', value: string}
interface IBarProps {type: 'bar', value: number}
const FooComponent: React.SFC<ExclusiveUnion<IFooProps, IBarProps>> = () => {
return (<div>Hello World</div>);
}`,
babelOptions
);

expect(result.code).toBe(`import React from 'react';
import PropTypes from "prop-types";
const FooComponent = () => {
return <div>Hello World</div>;
};
FooComponent.propTypes = {
type: PropTypes.oneOfType([PropTypes.oneOf(["foo"]), PropTypes.oneOf(["bar"])]),
value: PropTypes.oneOfType([PropTypes.string.isRequired, PropTypes.number.isRequired]).isRequired
};`);
});

});

describe('array / arrayOf propTypes', () => {
Expand Down

1 comment on commit 2a3f97a

@cchaos
Copy link
Contributor

@cchaos cchaos commented on 2a3f97a Feb 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @chandlerprall !

Please sign in to comment.