Skip to content

Commit 8f8d09b

Browse files
jquensedanez
authored andcommitted
fix: type inference on forwardRef components (#392)
1 parent e05219b commit 8f8d09b

File tree

5 files changed

+89
-25
lines changed

5 files changed

+89
-25
lines changed

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

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,11 @@ Object {
982982
"props": Object {
983983
"color": Object {
984984
"description": "",
985-
"required": true,
985+
"flowType": Object {
986+
"name": "string",
987+
"nullable": true,
988+
},
989+
"required": false,
986990
"type": Object {
987991
"name": "string",
988992
},
@@ -1421,3 +1425,24 @@ Object {
14211425
},
14221426
}
14231427
`;
1428+
1429+
exports[`main fixtures processes component "component_28.tsx" without errors 1`] = `
1430+
Object {
1431+
"description": "Example component description",
1432+
"displayName": "ABC",
1433+
"methods": Array [],
1434+
"props": Object {
1435+
"foo": Object {
1436+
"defaultValue": Object {
1437+
"computed": false,
1438+
"value": "true",
1439+
},
1440+
"description": "Example prop description",
1441+
"required": false,
1442+
"tsType": Object {
1443+
"name": "boolean",
1444+
},
1445+
},
1446+
},
1447+
}
1448+
`;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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 from 'react';
10+
11+
interface Props {
12+
/**
13+
* Example prop description
14+
*/
15+
foo: boolean;
16+
}
17+
18+
/**
19+
* Example component description
20+
*/
21+
const Component = React.forwardRef(({ foo = true }: Props, ref: any) => {
22+
return <div />;
23+
})
24+
25+
Component.displayName = 'ABC';
26+
27+
export default Component;

src/__tests__/main-test.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import fs from 'fs';
1010
import path from 'path';
11-
import { parse, handlers } from '../main';
11+
import { handlers, parse } from '../main';
1212
import { ERROR_MISSING_DEFINITION } from '../parse';
1313

1414
describe('main', () => {
@@ -45,7 +45,7 @@ describe('main', () => {
4545

4646
describe('React.createClass', () => {
4747
test(`
48-
var React = require("React");
48+
var React = require("react");
4949
var PropTypes = React.PropTypes;
5050
5151
var defaultProps = {
@@ -74,7 +74,7 @@ describe('main', () => {
7474

7575
describe('Class definition', () => {
7676
test(`
77-
const React = require("React");
77+
const React = require("react");
7878
const PropTypes = React.PropTypes;
7979
8080
const defaultProps = {
@@ -101,7 +101,7 @@ describe('main', () => {
101101

102102
describe('Stateless Component definition: ArrowFunctionExpression', () => {
103103
test(`
104-
import React, {PropTypes} from "React";
104+
import React, {PropTypes} from "react";
105105
106106
const defaultProps = {
107107
foo: true,
@@ -127,7 +127,7 @@ describe('main', () => {
127127

128128
describe('Stateless Component definition: FunctionDeclaration', () => {
129129
test(`
130-
import React, {PropTypes} from "React";
130+
import React, {PropTypes} from "react";
131131
132132
const defaultProps = {
133133
foo: true,
@@ -156,7 +156,7 @@ describe('main', () => {
156156

157157
describe('Stateless Component definition: FunctionExpression', () => {
158158
test(`
159-
import React, {PropTypes} from "React";
159+
import React, {PropTypes} from "react";
160160
161161
const defaultProps = {
162162
foo: true,
@@ -186,7 +186,7 @@ describe('main', () => {
186186
describe('Stateless Component definition', () => {
187187
it('is not so greedy', () => {
188188
const source = `
189-
import React, {PropTypes} from "React";
189+
import React, {PropTypes} from "react";
190190
191191
/**
192192
* Example component description

src/handlers/flowTypeHandler.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@
88
*/
99

1010
import types from 'ast-types';
11+
import type Documentation from '../Documentation';
12+
import { unwrapUtilityType } from '../utils/flowUtilityTypes';
1113
import getFlowType from '../utils/getFlowType';
12-
import getTSType from '../utils/getTSType';
13-
import getPropertyName from '../utils/getPropertyName';
1414
import getFlowTypeFromReactComponent, {
1515
applyToFlowTypeProperties,
1616
} from '../utils/getFlowTypeFromReactComponent';
17+
import getPropertyName from '../utils/getPropertyName';
18+
import getTSType from '../utils/getTSType';
19+
import { type TypeParameters } from '../utils/getTypeParameters';
1720
import resolveToValue from '../utils/resolveToValue';
1821
import setPropDescription from '../utils/setPropDescription';
19-
import { unwrapUtilityType } from '../utils/flowUtilityTypes';
20-
import { type TypeParameters } from '../utils/getTypeParameters';
21-
import type Documentation from '../Documentation';
2222

2323
const { namedTypes: t } = types;
2424

src/utils/getFlowTypeFromReactComponent.js

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,30 @@
88
*/
99

1010
import type Documentation from '../Documentation';
11-
import getTypeAnnotation from '../utils/getTypeAnnotation';
12-
import getMemberValuePath from '../utils/getMemberValuePath';
13-
import isReactComponentClass from '../utils/isReactComponentClass';
14-
import isStatelessComponent from '../utils/isStatelessComponent';
15-
import resolveGenericTypeAnnotation from '../utils/resolveGenericTypeAnnotation';
16-
import getTypeParameters, {
17-
type TypeParameters,
18-
} from '../utils/getTypeParameters';
11+
import getMemberValuePath from './getMemberValuePath';
12+
import getTypeAnnotation from './getTypeAnnotation';
13+
import getTypeParameters, { type TypeParameters } from './getTypeParameters';
14+
import isReactComponentClass from './isReactComponentClass';
15+
import isReactForwardRefCall from './isReactForwardRefCall';
16+
import resolveGenericTypeAnnotation from './resolveGenericTypeAnnotation';
17+
import resolveToValue from './resolveToValue';
18+
19+
function getStatelessPropsPath(componentDefinition): NodePath {
20+
const value = resolveToValue(componentDefinition);
21+
if (isReactForwardRefCall(value)) {
22+
const inner = value.get('arguments', 0);
23+
return inner.get('params', 0);
24+
}
25+
return value.get('params', 0);
26+
}
1927

2028
/**
2129
* Given an React component (stateless or class) tries to find the
2230
* flow type for the props. If not found or not one of the supported
2331
* component types returns null.
2432
*/
2533
export default (path: NodePath): ?NodePath => {
26-
let typePath: ?NodePath;
34+
let typePath: ?NodePath = null;
2735

2836
if (isReactComponentClass(path)) {
2937
const superTypes = path.get('superTypeParameters');
@@ -43,10 +51,14 @@ export default (path: NodePath): ?NodePath => {
4351

4452
typePath = getTypeAnnotation(propsMemberPath.parentPath);
4553
}
46-
} else if (isStatelessComponent(path)) {
47-
const param = path.get('params').get(0);
4854

49-
typePath = getTypeAnnotation(param);
55+
return typePath;
56+
}
57+
58+
const propsParam = getStatelessPropsPath(path);
59+
60+
if (propsParam) {
61+
typePath = getTypeAnnotation(propsParam);
5062
}
5163

5264
return typePath;

0 commit comments

Comments
 (0)