Skip to content

Commit 07fc2cf

Browse files
authored
Add support for typeof types and React.* types (#224)
* Add support for typeof types and React.* types * Remove unused import
1 parent f198733 commit 07fc2cf

File tree

3 files changed

+72
-9
lines changed

3 files changed

+72
-9
lines changed

src/utils/__tests__/getFlowType-test.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,57 @@ describe('getFlowType', () => {
195195

196196
expect(getFlowType(typePath)).toEqual({ name: 'string' });
197197
});
198+
199+
it('handles typeof types', () => {
200+
var typePath = statement(`
201+
var x: typeof MyType = {};
202+
203+
type MyType = { a: string, b: ?xyz };
204+
`).get('declarations', 0).get('id').get('typeAnnotation').get('typeAnnotation');
205+
206+
expect(getFlowType(typePath)).toEqual({name: 'signature', type: 'object', signature: {
207+
properties: [
208+
{ key: 'a', value: { name: 'string', required: true } },
209+
{ key: 'b', value: { name: 'xyz', nullable: true, required: true } },
210+
],
211+
}, raw: '{ a: string, b: ?xyz }'});
212+
});
213+
214+
describe('React types', () => {
215+
function test(type, expected) {
216+
var typePath = statement(`
217+
var x: ${type} = 2;
218+
219+
type Props = { x: string };
220+
`).get('declarations', 0).get('id').get('typeAnnotation').get('typeAnnotation');
221+
222+
expect(getFlowType(typePath)).toEqual({ ...expected, name: type.replace('.', '').replace(/<.+>/, ''), raw: type });
223+
}
224+
225+
const types = {
226+
'React.Node': {},
227+
'React.Key': {},
228+
'React.ElementType': {},
229+
'React.ChildrenArray<string>': { 'elements': [{ 'name': 'string' } ] },
230+
'React.Element<any>': { 'elements': [{ 'name': 'any' } ] },
231+
'React.Ref<typeof Component>': { 'elements': [{ 'name': 'Component' } ] },
232+
'React.ElementProps<Component>': { 'elements': [{ 'name': 'Component' } ] },
233+
'React.ElementRef<Component>': { 'elements': [{ 'name': 'Component' } ] },
234+
'React.ComponentType<Props>': {
235+
'elements': [{
236+
'name': 'signature',
237+
'raw': '{ x: string }',
238+
'signature': {
239+
'properties': [{ 'key': 'x', 'value': { 'name': 'string', 'required': true } }],
240+
},
241+
'type': 'object',
242+
}]
243+
},
244+
'React.StatelessFunctionalComponent<Props2>': { 'elements': [{ 'name': 'Props2' } ] },
245+
};
246+
247+
Object.keys(types).forEach(type => {
248+
it(type, () => test(type, types[type]));
249+
});
250+
});
198251
});

src/utils/getFlowType.js

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import printValue from './printValue';
1717
import recast from 'recast';
1818
import getTypeAnnotation from '../utils/getTypeAnnotation';
1919
import resolveToValue from '../utils/resolveToValue';
20-
import isUnreachableFlowType from '../utils/isUnreachableFlowType';
2120

2221
const { types: { namedTypes: types } } = recast;
2322

@@ -44,6 +43,7 @@ const namedTypes = {
4443
FunctionTypeAnnotation: handleFunctionTypeAnnotation,
4544
IntersectionTypeAnnotation: handleIntersectionTypeAnnotation,
4645
TupleTypeAnnotation: handleTupleTypeAnnotation,
46+
TypeofTypeAnnotation: handleTypeofTypeAnnotation,
4747
};
4848

4949
function getFlowTypeWithRequirements(path: NodePath): FlowTypeDescriptor {
@@ -55,7 +55,12 @@ function getFlowTypeWithRequirements(path: NodePath): FlowTypeDescriptor {
5555
}
5656

5757
function handleGenericTypeAnnotation(path: NodePath) {
58-
let type = { name: path.node.id.name };
58+
let type;
59+
if (types.QualifiedTypeIdentifier.check(path.node.id)) {
60+
type = handleQualifiedTypeIdentifier(path.get('id'));
61+
} else {
62+
type = { name: path.node.id.name };
63+
}
5964

6065
if (path.node.typeParameters) {
6166
const params = path.get('typeParameters').get('params');
@@ -67,8 +72,7 @@ function handleGenericTypeAnnotation(path: NodePath) {
6772
};
6873
} else {
6974
let resolvedPath = resolveToValue(path.get('id'));
70-
71-
if (!isUnreachableFlowType(resolvedPath)) {
75+
if (resolvedPath && resolvedPath.node.right) {
7276
type = getFlowType(resolvedPath.get('right'));
7377
}
7478
}
@@ -166,6 +170,16 @@ function handleTupleTypeAnnotation(path: NodePath) {
166170
return type;
167171
}
168172

173+
function handleTypeofTypeAnnotation(path: NodePath) {
174+
return getFlowType(path.get('argument'));
175+
}
176+
177+
function handleQualifiedTypeIdentifier(path: NodePath) {
178+
if (path.node.qualification.name !== 'React') return;
179+
180+
return { name: `React${path.node.id.name}`, raw: printValue(path) };
181+
}
182+
169183
/**
170184
* Tries to identify the flow type by inspecting the path for known
171185
* flow type names. This method doesn't check whether the found type is actually

src/utils/getFlowTypeFromReactComponent.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,7 @@ function resolveGenericTypeAnnotation(path: NodePath): ?NodePath {
9595
let typePath: ?NodePath;
9696
if (path && types.GenericTypeAnnotation.check(path.node)) {
9797
typePath = resolveToValue(path.get('id'));
98-
if (
99-
!typePath ||
100-
types.Identifier.check(typePath.node) ||
101-
isUnreachableFlowType(typePath)
102-
) {
98+
if (isUnreachableFlowType(typePath)) {
10399
return;
104100
}
105101

0 commit comments

Comments
 (0)