Skip to content

Commit 683eac7

Browse files
✨ Add support for memo typed with generics (#159)
* ✨ Add support for memo typed with generics * docs(changeset): Add support for memos typed with generics
1 parent e54d851 commit 683eac7

File tree

5 files changed

+124
-12
lines changed

5 files changed

+124
-12
lines changed

.changeset/shiny-cows-share.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'extract-react-types': minor
3+
---
4+
5+
Add support for memos typed with generics

packages/extract-react-types/__snapshots__/converters-typescript.test.js.snap

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,62 @@ Object {
396396
}
397397
`;
398398

399+
exports[`TypeScript: memo typed via generic types 1`] = `
400+
Object {
401+
"kind": "generic",
402+
"name": Object {
403+
"kind": "id",
404+
"name": "MyComponent",
405+
"type": null,
406+
},
407+
"value": Object {
408+
"kind": "object",
409+
"members": Array [
410+
Object {
411+
"key": Object {
412+
"kind": "id",
413+
"name": "foo",
414+
},
415+
"kind": "property",
416+
"optional": false,
417+
"value": Object {
418+
"kind": "string",
419+
},
420+
},
421+
],
422+
"referenceIdName": "MyComponentProps",
423+
},
424+
}
425+
`;
426+
427+
exports[`TypeScript: memo wrapping forwardRef, both typed via generic types 1`] = `
428+
Object {
429+
"kind": "generic",
430+
"name": Object {
431+
"kind": "id",
432+
"name": "MyComponent",
433+
"type": null,
434+
},
435+
"value": Object {
436+
"kind": "object",
437+
"members": Array [
438+
Object {
439+
"key": Object {
440+
"kind": "id",
441+
"name": "foo",
442+
},
443+
"kind": "property",
444+
"optional": false,
445+
"value": Object {
446+
"kind": "string",
447+
},
448+
},
449+
],
450+
"referenceIdName": "MyComponentProps",
451+
},
452+
}
453+
`;
454+
399455
exports[`TypeScript: ts any 1`] = `
400456
Object {
401457
"kind": "object",

packages/extract-react-types/converters-typescript.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,40 @@ const TESTS = [
567567
568568
export default MyComponent;
569569
`
570+
},
571+
{
572+
name: 'memo typed via generic types',
573+
typeSystem: 'typescript',
574+
code: `
575+
import React, { forwardRef, memo } from 'react';
576+
577+
type MyComponentProps = {
578+
foo: string,
579+
}
580+
581+
const MyComponent = memo<MyComponentProps>((props, ref) => {
582+
return <span>Foo</span>;
583+
});
584+
585+
export default MyComponent;
586+
`
587+
},
588+
{
589+
name: 'memo wrapping forwardRef, both typed via generic types',
590+
typeSystem: 'typescript',
591+
code: `
592+
import React, { forwardRef, memo } from 'react';
593+
594+
type MyComponentProps = {
595+
foo: string,
596+
}
597+
598+
const MyComponent = memo<MyComponentProps>(forwardRef<HTMLElement, MyComponentProps>((props, ref) => {
599+
return <span>Foo</span>;
600+
}));
601+
602+
export default MyComponent;
603+
`
570604
}
571605
];
572606

packages/extract-react-types/src/findExports.js

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
import { loadFileSync, resolveImportFilePathSync } from 'babel-file-loader';
2+
23
export function hasDestructuredDefaultExport(path) {
3-
const exportPath = path.get('body').find(bodyPath => {
4-
return (
5-
bodyPath.isExportNamedDeclaration() &&
6-
bodyPath.get('specifiers').filter(n => n.node.exported.name === 'default').length
4+
const exportPath = path
5+
.get('body')
6+
.find(
7+
bodyPath =>
8+
bodyPath.isExportNamedDeclaration() &&
9+
bodyPath.get('specifiers').filter(n => n.node.exported.name === 'default').length
710
);
8-
});
911

1012
return Boolean(exportPath);
1113
}
1214

1315
export function followExports(path, context, convert) {
14-
const exportPath = path.get('body').find(bodyPath => {
15-
return (
16-
bodyPath.isExportNamedDeclaration() &&
17-
bodyPath.get('specifiers').filter(n => n.node.exported.name === 'default')
16+
const exportPath = path
17+
.get('body')
18+
.find(
19+
bodyPath =>
20+
bodyPath.isExportNamedDeclaration() &&
21+
bodyPath.get('specifiers').filter(n => n.node.exported.name === 'default')
1822
);
19-
});
2023

21-
if (!exportPath)
24+
if (!exportPath) {
2225
throw new Error({
2326
message: 'No export path found'
2427
});
28+
}
2529

2630
try {
2731
const filePath = resolveImportFilePathSync(exportPath, context.resolveOptions);

packages/extract-react-types/src/index.js

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1366,6 +1366,7 @@ function exportedComponents(programPath, componentsToFind: 'all' | 'default', co
13661366
components.push({ name, path, component });
13671367
return;
13681368
}
1369+
13691370
if (path.isClass()) {
13701371
let component = convertReactComponentClass(path, context);
13711372
components.push({ name, path, component });
@@ -1380,7 +1381,7 @@ function exportedComponents(programPath, componentsToFind: 'all' | 'default', co
13801381
const genericTypeParams = path.get('typeParameters');
13811382

13821383
// Props are the second type arg
1383-
if (isForwardRef && genericTypeParams && genericTypeParams.node) {
1384+
if (isForwardRef && genericTypeParams.node) {
13841385
const component = convertReactComponentFunction(
13851386
genericTypeParams,
13861387
context,
@@ -1392,6 +1393,18 @@ function exportedComponents(programPath, componentsToFind: 'all' | 'default', co
13921393
return;
13931394
}
13941395

1396+
if (isMemo && genericTypeParams.node) {
1397+
const component = convertReactComponentFunction(
1398+
genericTypeParams,
1399+
context,
1400+
genericTypeParams.get('params.0')
1401+
);
1402+
1403+
components.push({ name, path, component });
1404+
1405+
return;
1406+
}
1407+
13951408
// Props typed via function arguments
13961409
const firstArg = path.get('arguments')[0];
13971410

0 commit comments

Comments
 (0)