Skip to content

Commit 6747dff

Browse files
committed
Support typescript and flow interfaces
1 parent ea424ae commit 6747dff

File tree

6 files changed

+232
-9
lines changed

6 files changed

+232
-9
lines changed

src/__tests__/__snapshots__/main-test.js.snap

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,3 +1176,131 @@ Object {
11761176
},
11771177
}
11781178
`;
1179+
1180+
exports[`main fixtures processes component "component_23.tsx" without errors 1`] = `
1181+
Object {
1182+
"description": "This is a typescript class component",
1183+
"displayName": "TSComponent",
1184+
"methods": Array [],
1185+
"props": Object {
1186+
"bar": Object {
1187+
"description": "Required prop",
1188+
"flowType": Object {
1189+
"name": "number",
1190+
},
1191+
"required": true,
1192+
},
1193+
"baz": Object {
1194+
"description": "Complex union prop",
1195+
"flowType": Object {
1196+
"elements": Array [
1197+
Object {
1198+
"name": "number",
1199+
},
1200+
Object {
1201+
"name": "signature",
1202+
"raw": "{ enter?: number, exit?: number }",
1203+
"signature": Object {
1204+
"properties": Array [
1205+
Object {
1206+
"key": "enter",
1207+
"value": Object {
1208+
"name": "number",
1209+
"required": false,
1210+
},
1211+
},
1212+
Object {
1213+
"key": "exit",
1214+
"value": Object {
1215+
"name": "number",
1216+
"required": false,
1217+
},
1218+
},
1219+
],
1220+
},
1221+
"type": "object",
1222+
},
1223+
Object {
1224+
"name": "literal",
1225+
"value": "'auto'",
1226+
},
1227+
],
1228+
"name": "union",
1229+
"raw": "number | { enter?: number, exit?: number } | 'auto'",
1230+
},
1231+
"required": true,
1232+
},
1233+
"foo": Object {
1234+
"description": "Optional prop",
1235+
"flowType": Object {
1236+
"name": "string",
1237+
},
1238+
"required": false,
1239+
},
1240+
},
1241+
}
1242+
`;
1243+
1244+
exports[`main fixtures processes component "component_24.js" without errors 1`] = `
1245+
Object {
1246+
"description": "This is a typescript class component",
1247+
"displayName": "TSComponent",
1248+
"methods": Array [],
1249+
"props": Object {
1250+
"bar": Object {
1251+
"description": "Required prop",
1252+
"flowType": Object {
1253+
"name": "number",
1254+
},
1255+
"required": true,
1256+
},
1257+
"baz": Object {
1258+
"description": "Complex union prop",
1259+
"flowType": Object {
1260+
"elements": Array [
1261+
Object {
1262+
"name": "number",
1263+
},
1264+
Object {
1265+
"name": "signature",
1266+
"raw": "{ enter?: number, exit?: number }",
1267+
"signature": Object {
1268+
"properties": Array [
1269+
Object {
1270+
"key": "enter",
1271+
"value": Object {
1272+
"name": "number",
1273+
"required": false,
1274+
},
1275+
},
1276+
Object {
1277+
"key": "exit",
1278+
"value": Object {
1279+
"name": "number",
1280+
"required": false,
1281+
},
1282+
},
1283+
],
1284+
},
1285+
"type": "object",
1286+
},
1287+
Object {
1288+
"name": "literal",
1289+
"value": "'auto'",
1290+
},
1291+
],
1292+
"name": "union",
1293+
"raw": "number | { enter?: number, exit?: number } | 'auto'",
1294+
},
1295+
"required": true,
1296+
},
1297+
"foo": Object {
1298+
"description": "Optional prop",
1299+
"flowType": Object {
1300+
"name": "string",
1301+
},
1302+
"required": false,
1303+
},
1304+
},
1305+
}
1306+
`;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import React, { Component } from 'react';
10+
11+
interface BaseProps {
12+
/** Optional prop */
13+
foo?: string,
14+
/** Required prop */
15+
bar: number
16+
}
17+
18+
type TransitionDuration = number | { enter?: number, exit?: number } | 'auto';
19+
20+
interface Props extends BaseProps {
21+
/** Complex union prop */
22+
baz: TransitionDuration
23+
}
24+
25+
/**
26+
* This is a typescript class component
27+
*/
28+
export default class TSComponent extends Component<Props> {
29+
render() {
30+
return <h1>Hello world</h1>;
31+
}
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
*/
8+
9+
import React, { Component } from 'react';
10+
11+
interface BaseProps {
12+
/** Optional prop */
13+
foo?: string,
14+
/** Required prop */
15+
bar: number
16+
}
17+
18+
type TransitionDuration = number | { enter?: number, exit?: number } | 'auto';
19+
20+
interface Props extends BaseProps {
21+
/** Complex union prop */
22+
baz: TransitionDuration
23+
}
24+
25+
/**
26+
* This is a flow class component with an interface as props
27+
*/
28+
export default class FlowComponent extends Component<Props> {
29+
render() {
30+
return <h1>Hello world</h1>;
31+
}
32+
}

src/utils/getFlowTypeFromReactComponent.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,18 @@ export function applyToFlowTypeProperties(
5656
path.get('properties').each(propertyPath => callback(propertyPath));
5757
} else if (path.node.members) {
5858
path.get('members').each(propertyPath => callback(propertyPath));
59+
} else if (path.node.type === 'InterfaceDeclaration') {
60+
if (path.node.extends) {
61+
applyExtends(path, callback);
62+
}
63+
64+
path.get('body', 'properties').each(propertyPath => callback(propertyPath));
65+
} else if (path.node.type === 'TSInterfaceDeclaration') {
66+
if (path.node.extends) {
67+
applyExtends(path, callback);
68+
}
69+
70+
path.get('body', 'body').each(propertyPath => callback(propertyPath));
5971
} else if (
6072
path.node.type === 'IntersectionTypeAnnotation' ||
6173
path.node.type === 'TSIntersectionType'
@@ -72,3 +84,12 @@ export function applyToFlowTypeProperties(
7284
}
7385
}
7486
}
87+
88+
function applyExtends(path, callback) {
89+
path.get('extends').each((extendsPath: NodePath) => {
90+
const resolvedPath = resolveGenericTypeAnnotation(extendsPath);
91+
if (resolvedPath) {
92+
applyToFlowTypeProperties(resolvedPath, callback);
93+
}
94+
});
95+
}

src/utils/resolveGenericTypeAnnotation.js

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,29 @@ const {
1818

1919
function tryResolveGenericTypeAnnotation(path: NodePath): ?NodePath {
2020
let typePath = unwrapUtilityType(path);
21+
let idPath;
2122

22-
if (types.GenericTypeAnnotation.check(typePath.node)) {
23-
typePath = resolveToValue(typePath.get('id'));
23+
if (typePath.node.id) {
24+
idPath = typePath.get('id');
25+
} else if (types.TSTypeReference.check(typePath.node)) {
26+
idPath = typePath.get('typeName');
27+
} else if (types.TSExpressionWithTypeArguments.check(typePath.node)) {
28+
idPath = typePath.get('expression');
29+
}
30+
31+
if (idPath) {
32+
typePath = resolveToValue(idPath);
2433
if (isUnreachableFlowType(typePath)) {
2534
return;
2635
}
2736

28-
return tryResolveGenericTypeAnnotation(typePath.get('right'));
29-
} else if (types.TSTypeReference.check(typePath.node)) {
30-
typePath = resolveToValue(typePath.get('typeName'));
31-
if (isUnreachableFlowType(typePath)) {
32-
return;
37+
if (types.TypeAlias.check(typePath.node)) {
38+
return tryResolveGenericTypeAnnotation(typePath.get('right'));
39+
} else if (types.TSTypeAliasDeclaration.check(typePath.node)) {
40+
return tryResolveGenericTypeAnnotation(typePath.get('typeAnnotation'));
3341
}
3442

35-
return tryResolveGenericTypeAnnotation(typePath.get('typeAnnotation'));
43+
return typePath;
3644
}
3745

3846
return typePath;

src/utils/resolveToValue.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,9 @@ function findScopePath(paths: Array<NodePath>, path: NodePath): ?NodePath {
5252
types.ImportNamespaceSpecifier.check(parentPath.node) ||
5353
types.VariableDeclarator.check(parentPath.node) ||
5454
types.TypeAlias.check(parentPath.node) ||
55-
types.TSTypeAliasDeclaration.check(parentPath.node)
55+
types.InterfaceDeclaration.check(parentPath.node) ||
56+
types.TSTypeAliasDeclaration.check(parentPath.node) ||
57+
types.TSInterfaceDeclaration.check(parentPath.node)
5658
) {
5759
resultPath = parentPath;
5860
} else if (types.Property.check(parentPath.node)) {

0 commit comments

Comments
 (0)