diff --git a/.changeset/nested-method-signatures.md b/.changeset/nested-method-signatures.md new file mode 100644 index 00000000000..e54a40a58d7 --- /dev/null +++ b/.changeset/nested-method-signatures.md @@ -0,0 +1,5 @@ +--- +'react-docgen': patch +--- + +Preserve function arguments and return types for TypeScript method signatures in nested object types. diff --git a/packages/react-docgen/src/utils/__tests__/getTSType-test.ts b/packages/react-docgen/src/utils/__tests__/getTSType-test.ts index 0413d2b8503..2dc92c50677 100644 --- a/packages/react-docgen/src/utils/__tests__/getTSType-test.ts +++ b/packages/react-docgen/src/utils/__tests__/getTSType-test.ts @@ -304,6 +304,32 @@ describe('getTSType', () => { expect(getTSType(typePath)).toMatchSnapshot(); }); + test.each([ + ['run(value: string): number', true], + ['run?(value: string): number', false], + ])('detects nested method signatures: %s', (method, required) => { + const typePath = typeAlias(`let x: { ${method} };`); + + expect(getTSType(typePath)).toMatchObject({ + signature: { + properties: [ + { + key: 'run', + value: { + name: 'signature', + type: 'function', + required, + signature: { + arguments: [{ name: 'value', type: { name: 'string' } }], + return: { name: 'number' }, + }, + }, + }, + ], + }, + }); + }); + test('detects callable signature type', () => { const typePath = typeAlias( 'let x: { (str: string): string, token: string };', diff --git a/packages/react-docgen/src/utils/getTSType.ts b/packages/react-docgen/src/utils/getTSType.ts index 91346f11915..c44ea3ab063 100644 --- a/packages/react-docgen/src/utils/getTSType.ts +++ b/packages/react-docgen/src/utils/getTSType.ts @@ -18,6 +18,7 @@ import type { NodePath } from '@babel/traverse'; import type { TSUnionType, TSFunctionType, + TSMethodSignature, TSIntersectionType, TSMappedType, TSTupleType, @@ -216,9 +217,18 @@ function handleTSTypeLiteral( doc = { description: docblock }; } + let value: TypeDescriptor; + + if (param.isTSMethodSignature()) { + value = handleTSFunctionType(param, typeParams); + value.required = !param.node.optional; + } else { + value = getTSTypeWithRequirements(typeAnnotation, typeParams); + } + type.signature.properties.push({ key: propName, - value: getTSTypeWithRequirements(typeAnnotation, typeParams), + value, ...doc, }); } else if (param.isTSCallSignatureDeclaration()) { @@ -317,7 +327,9 @@ function handleTSMappedType( } function handleTSFunctionType( - path: NodePath, + path: NodePath< + TSCallSignatureDeclaration | TSFunctionType | TSMethodSignature + >, typeParams: TypeParameters | null, ): TSFunctionSignatureType { let returnType: TypeDescriptor | undefined;