Skip to content

Commit

Permalink
Add support for flow and typescript generic type parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
devongovett committed Apr 28, 2019
1 parent 36fc0a1 commit 4cb1f3b
Show file tree
Hide file tree
Showing 10 changed files with 557 additions and 106 deletions.
74 changes: 74 additions & 0 deletions src/__tests__/__snapshots__/main-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1310,3 +1310,77 @@ Object {
},
}
`;
exports[`main fixtures processes component "component_25.tsx" without errors 1`] = `
Object {
"description": "This is a typescript class component",
"displayName": "TSComponent",
"methods": Array [],
"props": Object {
"bar": Object {
"description": "Required prop",
"required": true,
"tsType": Object {
"elements": Array [
Object {
"name": "Child",
},
],
"name": "Array",
"raw": "Array<X>",
},
},
"baz": Object {
"description": "Complex union prop",
"required": true,
"tsType": Object {
"name": "number",
},
},
"foo": Object {
"description": "Optional prop",
"required": false,
"tsType": Object {
"name": "Child",
},
},
},
}
`;
exports[`main fixtures processes component "component_26.js" without errors 1`] = `
Object {
"description": "This is a typescript class component",
"displayName": "FlowComponent",
"methods": Array [],
"props": Object {
"bar": Object {
"description": "Required prop",
"flowType": Object {
"elements": Array [
Object {
"name": "Child",
},
],
"name": "Array",
"raw": "Array<X>",
},
"required": true,
},
"baz": Object {
"description": "Complex union prop",
"flowType": Object {
"name": "number",
},
"required": true,
},
"foo": Object {
"description": "Optional prop",
"flowType": Object {
"name": "Child",
},
"required": false,
},
},
}
`;
33 changes: 33 additions & 0 deletions src/__tests__/fixtures/component_25.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* 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, { Component } from 'react';

type Test<X> = Array<X>;
interface BaseProps<T> {
/** Optional prop */
foo?: T,
/** Required prop */
bar: Test<T>
}

interface Child {}

interface Props extends BaseProps<Child> {
/** Complex union prop */
baz: number
}

/**
* This is a typescript class component
*/
export default class TSComponent extends Component<Props> {
render() {
return <h1>Hello world</h1>;
}
}
33 changes: 33 additions & 0 deletions src/__tests__/fixtures/component_26.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/**
* 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, { Component } from 'react';

type Test<X> = Array<X>;
interface BaseProps<T> {
/** Optional prop */
foo?: T,
/** Required prop */
bar: Test<T>
}

interface Child {}

interface Props extends BaseProps<Child> {
/** Complex union prop */
baz: number
}

/**
* This is a typescript class component
*/
export default class FlowComponent extends Component<Props> {
render() {
return <h1>Hello world</h1>;
}
}
43 changes: 31 additions & 12 deletions src/handlers/flowTypeHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,28 @@ import getFlowTypeFromReactComponent, {
import resolveToValue from '../utils/resolveToValue';
import setPropDescription from '../utils/setPropDescription';
import { unwrapUtilityType } from '../utils/flowUtilityTypes';
import { type TypeParameters } from '../utils/getTypeParameters';

const {
types: { namedTypes: types },
} = recast;
function setPropDescriptor(documentation: Documentation, path: NodePath): void {
function setPropDescriptor(
documentation: Documentation,
path: NodePath,
typeParams: ?TypeParameters,
): void {
if (types.ObjectTypeSpreadProperty.check(path.node)) {
const argument = unwrapUtilityType(path.get('argument'));

if (types.ObjectTypeAnnotation.check(argument.node)) {
applyToFlowTypeProperties(documentation, argument, propertyPath => {
setPropDescriptor(documentation, propertyPath);
});
applyToFlowTypeProperties(
documentation,
argument,
(propertyPath, innerTypeParams) => {
setPropDescriptor(documentation, propertyPath, innerTypeParams);
},
typeParams,
);
return;
}

Expand All @@ -39,14 +49,19 @@ function setPropDescriptor(documentation: Documentation, path: NodePath): void {

if (resolvedPath && types.TypeAlias.check(resolvedPath.node)) {
const right = resolvedPath.get('right');
applyToFlowTypeProperties(documentation, right, propertyPath => {
setPropDescriptor(documentation, propertyPath);
});
applyToFlowTypeProperties(
documentation,
right,
(propertyPath, innerTypeParams) => {
setPropDescriptor(documentation, propertyPath, innerTypeParams);
},
typeParams,
);
} else {
documentation.addComposes(name.node.name);
}
} else if (types.ObjectTypeProperty.check(path.node)) {
const type = getFlowType(path.get('value'));
const type = getFlowType(path.get('value'), typeParams);
const propName = getPropertyName(path);
if (!propName) return;

Expand All @@ -59,7 +74,7 @@ function setPropDescriptor(documentation: Documentation, path: NodePath): void {
// imported types that are spread in to props.
setPropDescription(documentation, path);
} else if (types.TSPropertySignature.check(path.node)) {
const type = getTSType(path.get('typeAnnotation'));
const type = getTSType(path.get('typeAnnotation'), typeParams);

const propName = getPropertyName(path);
if (!propName) return;
Expand Down Expand Up @@ -90,7 +105,11 @@ export default function flowTypeHandler(
return;
}

applyToFlowTypeProperties(documentation, flowTypesPath, propertyPath => {
setPropDescriptor(documentation, propertyPath);
});
applyToFlowTypeProperties(
documentation,
flowTypesPath,
(propertyPath, typeParams) => {
setPropDescriptor(documentation, propertyPath, typeParams);
},
);
}
38 changes: 38 additions & 0 deletions src/utils/__tests__/getFlowType-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,44 @@ describe('getFlowType', () => {
});
});

it('handles generic types', () => {
const typePath = statement(`
var x: MyType<string> = {};
type MyType<T> = { a: T, b: Array<T> };
`)
.get('declarations', 0)
.get('id')
.get('typeAnnotation')
.get('typeAnnotation');

expect(getFlowType(typePath)).toEqual({
name: 'signature',
type: 'object',
raw: '{ a: T, b: Array<T> }',
signature: {
properties: [
{
key: 'a',
value: {
name: 'string',
required: true,
},
},
{
key: 'b',
value: {
name: 'Array',
raw: 'Array<T>',
required: true,
elements: [{ name: 'string' }],
},
},
],
},
});
});

describe('React types', () => {
function test(type, expected) {
const typePath = statement(`
Expand Down
38 changes: 38 additions & 0 deletions src/utils/__tests__/getTSType-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,44 @@ describe('getTSType', () => {
});
});

it('handles generic types', () => {
const typePath = statement(`
var x: MyType<string> = {};
type MyType<T> = { a: T, b: Array<T> };
`)
.get('declarations', 0)
.get('id')
.get('typeAnnotation')
.get('typeAnnotation');

expect(getTSType(typePath)).toEqual({
name: 'signature',
type: 'object',
raw: '{ a: T, b: Array<T> }',
signature: {
properties: [
{
key: 'a',
value: {
name: 'string',
required: true,
},
},
{
key: 'b',
value: {
name: 'Array',
raw: 'Array<T>',
required: true,
elements: [{ name: 'string' }],
},
},
],
},
});
});

describe('React types', () => {
function test(type, expected) {
const typePath = statement(`
Expand Down

0 comments on commit 4cb1f3b

Please sign in to comment.