From 970be9feabdb2d9fb7f789d0e091a728c947df0f Mon Sep 17 00:00:00 2001 From: Devon Govett Date: Sat, 27 Apr 2019 20:40:31 -0700 Subject: [PATCH] Support rest parameters in function signatures --- src/utils/__tests__/getFlowType-test.js | 15 +++++++++++++-- src/utils/__tests__/getTSType-test.js | 14 ++++++++++++-- src/utils/getFlowType.js | 17 +++++++++++++++-- src/utils/getTSType.js | 13 ++++++++++--- 4 files changed, 50 insertions(+), 9 deletions(-) diff --git a/src/utils/__tests__/getFlowType-test.js b/src/utils/__tests__/getFlowType-test.js index 177b848784f..dacbe24ca39 100644 --- a/src/utils/__tests__/getFlowType-test.js +++ b/src/utils/__tests__/getFlowType-test.js @@ -216,7 +216,9 @@ describe('getFlowType', () => { }); it('detects function signature type', () => { - const typePath = expression('x: (p1: number, p2: ?string) => boolean') + const typePath = expression( + 'x: (p1: number, p2: ?string, ...rest: Array) => boolean', + ) .get('typeAnnotation') .get('typeAnnotation'); expect(getFlowType(typePath)).toEqual({ @@ -226,10 +228,18 @@ describe('getFlowType', () => { arguments: [ { name: 'p1', type: { name: 'number' } }, { name: 'p2', type: { name: 'string', nullable: true } }, + { + name: 'rest', + type: { + name: 'Array', + elements: [{ name: 'string' }], + raw: 'Array', + }, + }, ], return: { name: 'boolean' }, }, - raw: '(p1: number, p2: ?string) => boolean', + raw: '(p1: number, p2: ?string, ...rest: Array) => boolean', }); }); @@ -265,6 +275,7 @@ describe('getFlowType', () => { raw: 'string => boolean', }); }); + it('detects callable signature type', () => { const typePath = expression('x: { (str: string): string, token: string }') .get('typeAnnotation') diff --git a/src/utils/__tests__/getTSType-test.js b/src/utils/__tests__/getTSType-test.js index 3248647d09f..b3a1955d5c8 100644 --- a/src/utils/__tests__/getTSType-test.js +++ b/src/utils/__tests__/getTSType-test.js @@ -185,7 +185,9 @@ describe('getTSType', () => { }); it('detects function signature type', () => { - const typePath = expression('x: (p1: number, p2: string) => boolean') + const typePath = expression( + 'x: (p1: number, p2: string, ...rest: Array) => boolean', + ) .get('typeAnnotation') .get('typeAnnotation'); expect(getTSType(typePath)).toEqual({ @@ -195,10 +197,18 @@ describe('getTSType', () => { arguments: [ { name: 'p1', type: { name: 'number' } }, { name: 'p2', type: { name: 'string' } }, + { + name: 'rest', + type: { + name: 'Array', + elements: [{ name: 'string' }], + raw: 'Array', + }, + }, ], return: { name: 'boolean' }, }, - raw: '(p1: number, p2: string) => boolean', + raw: '(p1: number, p2: string, ...rest: Array) => boolean', }); }); diff --git a/src/utils/getFlowType.js b/src/utils/getFlowType.js index 8dff69f7ef0..21dd2f5bf24 100644 --- a/src/utils/getFlowType.js +++ b/src/utils/getFlowType.js @@ -266,14 +266,27 @@ function handleFunctionTypeAnnotation( path.get('params').each(param => { const typeAnnotation = getTypeAnnotation(param); - if (!typeAnnotation) return; type.signature.arguments.push({ name: param.node.name ? param.node.name.name : '', - type: getFlowTypeWithResolvedTypes(typeAnnotation, typeParams), + type: typeAnnotation + ? getFlowTypeWithResolvedTypes(typeAnnotation, typeParams) + : null, }); }); + if (path.node.rest) { + const rest = path.get('rest'); + const typeAnnotation = getTypeAnnotation(rest); + + type.signature.arguments.push({ + name: rest.node.name ? rest.node.name.name : '', + type: typeAnnotation + ? getFlowTypeWithResolvedTypes(typeAnnotation, typeParams) + : null, + }); + } + return type; } diff --git a/src/utils/getTSType.js b/src/utils/getTSType.js index f9d020fc49c..5eb38a02884 100644 --- a/src/utils/getTSType.js +++ b/src/utils/getTSType.js @@ -30,6 +30,7 @@ const tsTypes = { TSAnyKeyword: 'any', TSBooleanKeyword: 'boolean', TSUnknownKeyword: 'unknown', + TSNeverKeyword: 'never', TSNullKeyword: 'null', TSUndefinedKeyword: 'undefined', TSNumberKeyword: 'number', @@ -227,11 +228,17 @@ function handleTSFunctionType( path.get('parameters').each(param => { const typeAnnotation = getTypeAnnotation(param); - if (!typeAnnotation) return; + + let name = param.node.name || ''; + if (param.node.type === 'RestElement') { + name = param.node.argument.name; + } type.signature.arguments.push({ - name: param.node.name || '', - type: getTSTypeWithResolvedTypes(typeAnnotation, typeParams), + name, + type: typeAnnotation + ? getTSTypeWithResolvedTypes(typeAnnotation, typeParams) + : null, }); });