Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: type inference on forwardRef components #392

Merged
merged 1 commit into from Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
27 changes: 26 additions & 1 deletion src/__tests__/__snapshots__/main-test.js.snap
Expand Up @@ -982,7 +982,11 @@ Object {
"props": Object {
"color": Object {
"description": "",
"required": true,
"flowType": Object {
"name": "string",
"nullable": true,
},
"required": false,
"type": Object {
"name": "string",
},
Expand Down Expand Up @@ -1421,3 +1425,24 @@ Object {
},
}
`;

exports[`main fixtures processes component "component_28.tsx" without errors 1`] = `
Object {
"description": "Example component description",
"displayName": "ABC",
"methods": Array [],
"props": Object {
"foo": Object {
"defaultValue": Object {
"computed": false,
"value": "true",
},
"description": "Example prop description",
"required": false,
"tsType": Object {
"name": "boolean",
},
},
},
}
`;
27 changes: 27 additions & 0 deletions src/__tests__/fixtures/component_28.tsx
@@ -0,0 +1,27 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import React from 'react';

interface Props {
/**
* Example prop description
*/
foo: boolean;
}

/**
* Example component description
*/
const Component = React.forwardRef(({ foo = true }: Props, ref: any) => {
return <div />;
})

Component.displayName = 'ABC';

export default Component;
14 changes: 7 additions & 7 deletions src/__tests__/main-test.js
Expand Up @@ -8,7 +8,7 @@

import fs from 'fs';
import path from 'path';
import { parse, handlers } from '../main';
import { handlers, parse } from '../main';
import { ERROR_MISSING_DEFINITION } from '../parse';

describe('main', () => {
Expand Down Expand Up @@ -45,7 +45,7 @@ describe('main', () => {

describe('React.createClass', () => {
test(`
var React = require("React");
var React = require("react");
var PropTypes = React.PropTypes;

var defaultProps = {
Expand Down Expand Up @@ -74,7 +74,7 @@ describe('main', () => {

describe('Class definition', () => {
test(`
const React = require("React");
const React = require("react");
const PropTypes = React.PropTypes;

const defaultProps = {
Expand All @@ -101,7 +101,7 @@ describe('main', () => {

describe('Stateless Component definition: ArrowFunctionExpression', () => {
test(`
import React, {PropTypes} from "React";
import React, {PropTypes} from "react";

const defaultProps = {
foo: true,
Expand All @@ -127,7 +127,7 @@ describe('main', () => {

describe('Stateless Component definition: FunctionDeclaration', () => {
test(`
import React, {PropTypes} from "React";
import React, {PropTypes} from "react";

const defaultProps = {
foo: true,
Expand Down Expand Up @@ -156,7 +156,7 @@ describe('main', () => {

describe('Stateless Component definition: FunctionExpression', () => {
test(`
import React, {PropTypes} from "React";
import React, {PropTypes} from "react";

const defaultProps = {
foo: true,
Expand Down Expand Up @@ -186,7 +186,7 @@ describe('main', () => {
describe('Stateless Component definition', () => {
it('is not so greedy', () => {
const source = `
import React, {PropTypes} from "React";
import React, {PropTypes} from "react";

/**
* Example component description
Expand Down
10 changes: 5 additions & 5 deletions src/handlers/flowTypeHandler.js
Expand Up @@ -8,17 +8,17 @@
*/

import types from 'ast-types';
import type Documentation from '../Documentation';
import { unwrapUtilityType } from '../utils/flowUtilityTypes';
import getFlowType from '../utils/getFlowType';
import getTSType from '../utils/getTSType';
import getPropertyName from '../utils/getPropertyName';
import getFlowTypeFromReactComponent, {
applyToFlowTypeProperties,
} from '../utils/getFlowTypeFromReactComponent';
import getPropertyName from '../utils/getPropertyName';
import getTSType from '../utils/getTSType';
import { type TypeParameters } from '../utils/getTypeParameters';
import resolveToValue from '../utils/resolveToValue';
import setPropDescription from '../utils/setPropDescription';
import { unwrapUtilityType } from '../utils/flowUtilityTypes';
import { type TypeParameters } from '../utils/getTypeParameters';
import type Documentation from '../Documentation';

const { namedTypes: t } = types;

Expand Down
36 changes: 24 additions & 12 deletions src/utils/getFlowTypeFromReactComponent.js
Expand Up @@ -8,22 +8,30 @@
*/

import type Documentation from '../Documentation';
import getTypeAnnotation from '../utils/getTypeAnnotation';
import getMemberValuePath from '../utils/getMemberValuePath';
import isReactComponentClass from '../utils/isReactComponentClass';
import isStatelessComponent from '../utils/isStatelessComponent';
import resolveGenericTypeAnnotation from '../utils/resolveGenericTypeAnnotation';
import getTypeParameters, {
type TypeParameters,
} from '../utils/getTypeParameters';
import getMemberValuePath from './getMemberValuePath';
import getTypeAnnotation from './getTypeAnnotation';
import getTypeParameters, { type TypeParameters } from './getTypeParameters';
import isReactComponentClass from './isReactComponentClass';
import isReactForwardRefCall from './isReactForwardRefCall';
import resolveGenericTypeAnnotation from './resolveGenericTypeAnnotation';
import resolveToValue from './resolveToValue';

function getStatelessPropsPath(componentDefinition): NodePath {
const value = resolveToValue(componentDefinition);
if (isReactForwardRefCall(value)) {
const inner = value.get('arguments', 0);
return inner.get('params', 0);
}
return value.get('params', 0);
}

/**
* Given an React component (stateless or class) tries to find the
* flow type for the props. If not found or not one of the supported
* component types returns null.
*/
export default (path: NodePath): ?NodePath => {
let typePath: ?NodePath;
let typePath: ?NodePath = null;

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

typePath = getTypeAnnotation(propsMemberPath.parentPath);
}
} else if (isStatelessComponent(path)) {
const param = path.get('params').get(0);

typePath = getTypeAnnotation(param);
return typePath;
}

const propsParam = getStatelessPropsPath(path);

if (propsParam) {
typePath = getTypeAnnotation(propsParam);
}

return typePath;
Expand Down