Skip to content

Commit 953386a

Browse files
author
Keyan Zhang
committed
use FlowFixMe for unrecognizable types
1 parent 8b15535 commit 953386a

File tree

5 files changed

+115
-88
lines changed

5 files changed

+115
-88
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ jscodeshift -t react-codemod/transforms/sort-comp.js <path>
135135
- Flow actually understands `propTypes` in `createClass` calls but not ES6 class components. Here the transformation logic is identical to [how](https://github.com/facebook/flow/blob/master/src/typing/statement.ml#L3526) Flow treats `propTypes`
136136
- Notice that `React.PropTypes` and Flow treat optional values differently
137137
- For example, `foo: React.PropTypes.number` is valid when you pass `{}`, `{foo: null}`, or `{foo: undefined}` as props. The equivalent in Flow is actually `foo?: ?number`; the question mark on the left hand side indicates `{}` is valid
138-
- For `propTypes` fields that can't be recognized by Flow, `any` will be used
138+
- For `propTypes` fields that can't be recognized by Flow, `$FlowFixMe` will be used
139139

140140
### Recast Options
141141

transforms/__testfixtures__/class-flow2.output.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@ const { func } = React.PropTypes
1515
*/
1616
class IndexRoute extends React.Component {
1717
props: {
18-
path?: any,
19-
component?: any,
20-
components?: any,
21-
getComponent?: any,
22-
getComponents?: any,
18+
path?: $FlowFixMe,
19+
component?: $FlowFixMe,
20+
components?: $FlowFixMe,
21+
getComponent?: $FlowFixMe,
22+
getComponents?: $FlowFixMe,
2323
};
2424

2525
static createRouteFromReactElement(element, parentRoute) {

transforms/__testfixtures__/class-flow3.output.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,15 @@ var optionalFuncShortHand = PropTypes.func;
2020

2121
class Component extends React.Component {
2222
props: {
23-
optionalFuncShortHand?: any,
24-
optionalNumber?: any,
23+
optionalFuncShortHand?: $FlowFixMe,
24+
optionalNumber?: $FlowFixMe,
2525
optionalObject?: ?Object,
26-
optionalString?: any,
26+
optionalString?: $FlowFixMe,
2727
optionalNode?: any,
2828
optionalElement?: any,
2929
optionalMessage?: ?Message,
3030
optionalEnum?: ?('News' | 'Photos' | 1 | true | null),
31-
optionalUnion?: any,
31+
optionalUnion?: $FlowFixMe,
3232
optionalArrayOf?: ?Array<number>,
3333
optionalObjectOf?: ?{[key: string]: ?number},
3434
optionalObjectWithShape?: ?{color?: ?string},

transforms/__testfixtures__/class-flow6.output.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,21 @@ const justNeedKeys = {
1010
class Component extends React.Component {
1111
props: {
1212
optionalMessage?: ?Message,
13-
optionalMessageOops?: any,
14-
optionalEnum?: any,
15-
optionalEnumOops?: any,
13+
optionalMessageOops?: $FlowFixMe,
14+
optionalEnum?: $FlowFixMe,
15+
optionalEnumOops?: $FlowFixMe,
1616
optionalUnion?: ?(string | number | Message),
17-
optionalUnionOops?: any,
18-
optionalUnionOops2?: any,
17+
optionalUnionOops?: $FlowFixMe,
18+
optionalUnionOops2?: $FlowFixMe,
1919
optionalArrayOf?: ?Array<number>,
2020
optionalObjectOf?: ?{[key: string]: ?number},
2121
optionalObjectWithShape?: ?{
2222
color?: ?string,
23-
fontSize?: any,
24-
name?: any,
23+
fontSize?: $FlowFixMe,
24+
name?: $FlowFixMe,
2525
},
26-
optionalObjectWithShapeOops?: any,
27-
optionalObjectWithShapeOops2?: any,
26+
optionalObjectWithShapeOops?: $FlowFixMe,
27+
optionalObjectWithShapeOops2?: $FlowFixMe,
2828
'is-literal-cool'?: ?boolean,
2929
'well-fine': number,
3030
};

transforms/class.js

Lines changed: 96 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,10 @@ module.exports = (file, api, options) => {
601601
// Flow!
602602

603603
const flowAnyType = j.anyTypeAnnotation();
604+
const flowFixMeType = j.genericTypeAnnotation(
605+
j.identifier('$FlowFixMe'),
606+
null
607+
);
604608

605609
const literalToFlowType = node => {
606610
switch (typeof node.value) {
@@ -612,25 +616,25 @@ module.exports = (file, api, options) => {
612616
return j.booleanLiteralTypeAnnotation(node.value, node.raw);
613617
case 'object': // we already know it's a NullLiteral here
614618
return j.nullLiteralTypeAnnotation();
615-
default:
616-
return flowAnyType; // meh
619+
default: // this should never happen
620+
return flowFixMeType;
617621
}
618622
};
619623

620624
const propTypeToFlowMapping = {
621625
// prim types
622-
any: flowAnyType,
626+
any: flowAnyType, // this is the only real any
623627
array: j.genericTypeAnnotation(
624628
j.identifier('Array'),
625629
j.typeParameterInstantiation([flowAnyType])
626630
),
627631
bool: j.booleanTypeAnnotation(),
628-
element: flowAnyType,
632+
element: flowAnyType, // flow does the same for `element` type in `propTypes`
629633
func: j.genericTypeAnnotation(
630634
j.identifier('Function'),
631635
null
632636
),
633-
node: flowAnyType,
637+
node: flowAnyType, // flow does the same for `node` type in `propTypes`
634638
number: j.numberTypeAnnotation(),
635639
object: j.genericTypeAnnotation(
636640
j.identifier('Object'),
@@ -664,7 +668,7 @@ module.exports = (file, api, options) => {
664668
const propTypeToFlowAnnotation = val => {
665669
let cursor = val;
666670
let isOptional = true;
667-
let typeResult = flowAnyType;
671+
let typeResult = flowFixMeType;
668672

669673
if ( // check `.isRequired` first
670674
cursor.type === 'MemberExpression' &&
@@ -675,68 +679,72 @@ module.exports = (file, api, options) => {
675679
cursor = cursor.object;
676680
}
677681

678-
if (cursor.type === 'CallExpression') { // type class
679-
const calleeName = cursor.callee.type === 'MemberExpression' ?
680-
cursor.callee.property.name :
681-
cursor.callee.name;
682-
683-
const constructor = propTypeToFlowMapping[calleeName];
684-
if (!constructor) {
685-
typeResult = flowAnyType;
686-
return [typeResult, isOptional];
687-
}
688-
689-
switch (cursor.callee.property.name) {
690-
case 'arrayOf': {
691-
const arg = cursor.arguments[0];
692-
typeResult = constructor(
693-
propTypeToFlowAnnotation(arg)[0]
694-
);
682+
switch (cursor.type) {
683+
case 'CallExpression': { // type class
684+
const calleeName = cursor.callee.type === 'MemberExpression' ?
685+
cursor.callee.property.name :
686+
cursor.callee.name;
687+
688+
const constructor = propTypeToFlowMapping[calleeName];
689+
if (!constructor) { // unknown type class
690+
// it's not necessary since `typeResult` defaults to `flowFixMeType`,
691+
// but it's more explicit this way
692+
typeResult = flowFixMeType;
695693
break;
696694
}
697-
case 'instanceOf': {
698-
const arg = cursor.arguments[0];
699-
if (arg.type !== 'Identifier') {
700-
typeResult = flowAnyType;
701-
break;
702-
}
703695

704-
typeResult = constructor(arg);
705-
break;
706-
}
707-
case 'objectOf': {
708-
const arg = cursor.arguments[0];
709-
const [valueType, isOptional] = propTypeToFlowAnnotation(arg);
710-
typeResult = constructor(valueType, isOptional);
711-
break;
712-
}
713-
case 'oneOf': {
714-
const argList = cursor.arguments[0].elements;
715-
if (!argList || !argList.every(node => node.type === 'Literal')) {
716-
typeResult = flowAnyType;
717-
} else {
696+
switch (cursor.callee.property.name) {
697+
case 'arrayOf': {
698+
const arg = cursor.arguments[0];
718699
typeResult = constructor(
719-
argList.map(literalToFlowType)
700+
propTypeToFlowAnnotation(arg)[0]
720701
);
702+
break;
721703
}
722-
break;
723-
}
724-
case 'oneOfType': {
725-
const argList = cursor.arguments[0].elements;
726-
if (!argList) {
727-
typeResult = flowAnyType;
728-
} else {
729-
typeResult = constructor(
730-
argList.map(arg => propTypeToFlowAnnotation(arg)[0])
731-
);
704+
case 'instanceOf': {
705+
const arg = cursor.arguments[0];
706+
if (arg.type !== 'Identifier') {
707+
typeResult = flowFixMeType;
708+
break;
709+
}
710+
711+
typeResult = constructor(arg);
712+
break;
732713
}
733-
break;
734-
}
735-
case 'shape': {
736-
const rawPropList = cursor.arguments[0].properties;
737-
if (!rawPropList) {
738-
typeResult = flowAnyType;
739-
} else {
714+
case 'objectOf': {
715+
const arg = cursor.arguments[0];
716+
const [valueType, isOptional] = propTypeToFlowAnnotation(arg);
717+
typeResult = constructor(valueType, isOptional);
718+
break;
719+
}
720+
case 'oneOf': {
721+
const argList = cursor.arguments[0].elements;
722+
if (!argList || !argList.every(node => node.type === 'Literal')) {
723+
typeResult = flowFixMeType;
724+
} else {
725+
typeResult = constructor(
726+
argList.map(literalToFlowType)
727+
);
728+
}
729+
break;
730+
}
731+
case 'oneOfType': {
732+
const argList = cursor.arguments[0].elements;
733+
if (!argList) {
734+
typeResult = flowFixMeType;
735+
} else {
736+
typeResult = constructor(
737+
argList.map(arg => propTypeToFlowAnnotation(arg)[0])
738+
);
739+
}
740+
break;
741+
}
742+
case 'shape': {
743+
const rawPropList = cursor.arguments[0].properties;
744+
if (!rawPropList) {
745+
typeResult = flowFixMeType;
746+
break;
747+
}
740748
const flowPropList = [];
741749
rawPropList.forEach(typeProp => {
742750
const keyIsLiteral = typeProp.key.type === 'Literal';
@@ -745,23 +753,41 @@ module.exports = (file, api, options) => {
745753
const [valueType, isOptional] = propTypeToFlowAnnotation(typeProp.value);
746754
flowPropList.push(j.objectTypeProperty(
747755
keyIsLiteral ? j.literal(name) : j.identifier(name),
748-
isOptional && valueType !== flowAnyType ?
756+
// it doesn't make sense to have `?any` or `?$FlowFixMe`
757+
isOptional && valueType !== flowAnyType && valueType !== flowFixMeType ?
749758
j.nullableTypeAnnotation(valueType) :
750759
valueType,
751760
isOptional
752761
));
753762
});
754763

755764
typeResult = constructor(flowPropList);
765+
break;
766+
}
767+
default: {
768+
break;
756769
}
770+
}
771+
break;
772+
}
773+
case 'MemberExpression': { // prim type
774+
if (cursor.property.type !== 'Identifier') { // unrecognizable
775+
typeResult = flowFixMeType;
757776
break;
758777
}
778+
779+
const maybeType = propTypeToFlowMapping[cursor.property.name];
780+
if (maybeType) {
781+
typeResult = propTypeToFlowMapping[cursor.property.name];
782+
} else { // type not found
783+
typeResult = flowFixMeType;
784+
}
785+
786+
break;
787+
}
788+
default: { // unrecognizable
789+
break;
759790
}
760-
} else if ( // prim type
761-
cursor.type === 'MemberExpression' &&
762-
cursor.property.type === 'Identifier'
763-
) {
764-
typeResult = propTypeToFlowMapping[cursor.property.name] || flowAnyType;
765791
}
766792

767793
return [typeResult, isOptional];
@@ -775,7 +801,7 @@ module.exports = (file, api, options) => {
775801
}
776802

777803
prop.value.properties.forEach(typeProp => {
778-
if (!typeProp.key) { // SpreadProperty
804+
if (!typeProp.key) { // stuff like SpreadProperty
779805
return;
780806
}
781807

@@ -785,7 +811,8 @@ module.exports = (file, api, options) => {
785811
const [valueType, isOptional] = propTypeToFlowAnnotation(typeProp.value);
786812
typePropertyList.push(j.objectTypeProperty(
787813
keyIsLiteral ? j.literal(name) : j.identifier(name),
788-
isOptional && valueType !== flowAnyType ?
814+
// it doesn't make sense to have `?any` or `?$FlowFixMe`
815+
isOptional && valueType !== flowAnyType && valueType !== flowFixMeType ?
789816
j.nullableTypeAnnotation(valueType) :
790817
valueType,
791818
isOptional

0 commit comments

Comments
 (0)