diff --git a/src/types.js b/src/types.js index 59b3c9c95f3..ff504122091 100644 --- a/src/types.js +++ b/src/types.js @@ -59,12 +59,18 @@ export type FlowElementsType = FlowBaseType & { elements: Array, }; +export type FlowFunctionArgumentType = { + name: string, + type: FlowTypeDescriptor, + rest?: boolean, +}; + export type FlowFunctionSignatureType = FlowBaseType & { name: 'signature', type: 'function', raw: string, signature: { - arguments: Array<{ name: string, type: FlowTypeDescriptor }>, + arguments: Array, return: FlowTypeDescriptor, }, }; diff --git a/src/utils/__tests__/getFlowType-test.js b/src/utils/__tests__/getFlowType-test.js index 177b848784f..5a7e11318bd 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,19 @@ describe('getFlowType', () => { arguments: [ { name: 'p1', type: { name: 'number' } }, { name: 'p2', type: { name: 'string', nullable: true } }, + { + name: 'rest', + rest: true, + 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 +276,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..997af1d787a 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,19 @@ describe('getTSType', () => { arguments: [ { name: 'p1', type: { name: 'number' } }, { name: 'p2', type: { name: 'string' } }, + { + name: 'rest', + rest: true, + 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..1aca5dc6d65 100644 --- a/src/utils/getFlowType.js +++ b/src/utils/getFlowType.js @@ -266,14 +266,28 @@ 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, + rest: true, + }); + } + return type; } diff --git a/src/utils/getTSType.js b/src/utils/getTSType.js index f9d020fc49c..5ed22bbe932 100644 --- a/src/utils/getTSType.js +++ b/src/utils/getTSType.js @@ -19,6 +19,7 @@ import type { FlowTypeDescriptor, FlowElementsType, FlowFunctionSignatureType, + FlowFunctionArgumentType, FlowObjectSignatureType, } from '../types'; @@ -30,6 +31,7 @@ const tsTypes = { TSAnyKeyword: 'any', TSBooleanKeyword: 'boolean', TSUnknownKeyword: 'unknown', + TSNeverKeyword: 'never', TSNullKeyword: 'null', TSUndefinedKeyword: 'undefined', TSNumberKeyword: 'number', @@ -227,12 +229,19 @@ function handleTSFunctionType( path.get('parameters').each(param => { const typeAnnotation = getTypeAnnotation(param); - if (!typeAnnotation) return; - - type.signature.arguments.push({ + const arg: FlowFunctionArgumentType = { name: param.node.name || '', - type: getTSTypeWithResolvedTypes(typeAnnotation, typeParams), - }); + type: typeAnnotation + ? getTSTypeWithResolvedTypes(typeAnnotation, typeParams) + : null, + }; + + if (param.node.type === 'RestElement') { + arg.name = param.node.argument.name; + arg.rest = true; + } + + type.signature.arguments.push(arg); }); return type;