diff --git a/src/utils/__tests__/__snapshots__/getFlowType-test.ts.snap b/src/utils/__tests__/__snapshots__/getFlowType-test.ts.snap new file mode 100644 index 00000000000..997e60feee3 --- /dev/null +++ b/src/utils/__tests__/__snapshots__/getFlowType-test.ts.snap @@ -0,0 +1,122 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`getFlowType handles ObjectTypeSpreadProperty 1`] = ` +Object { + "name": "signature", + "raw": "{| apple: string, banana: string, ...OtherFruits |}", + "signature": Object { + "properties": Array [ + Object { + "key": "apple", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "banana", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "orange", + "value": Object { + "name": "string", + "required": true, + }, + }, + ], + }, + "type": "object", +} +`; + +exports[`getFlowType handles ObjectTypeSpreadProperty from imported types 1`] = ` +Object { + "name": "signature", + "raw": "{| apple: string, banana: string, ...MyType |}", + "signature": Object { + "properties": Array [ + Object { + "key": "apple", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "banana", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "a", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "b", + "value": Object { + "name": "notImported", + "nullable": true, + "required": true, + }, + }, + ], + }, + "type": "object", +} +`; + +exports[`getFlowType handles nested ObjectTypeSpreadProperty 1`] = ` +Object { + "name": "signature", + "raw": "{| apple: string, banana: string, ...BreakfastFruits |}", + "signature": Object { + "properties": Array [ + Object { + "key": "apple", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "banana", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "mango", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "orange", + "value": Object { + "name": "string", + "required": true, + }, + }, + Object { + "key": "lemon", + "value": Object { + "name": "string", + "required": true, + }, + }, + ], + }, + "type": "object", +} +`; diff --git a/src/utils/__tests__/getFlowType-test.ts b/src/utils/__tests__/getFlowType-test.ts index 853571c07b6..e2c3e8fddac 100644 --- a/src/utils/__tests__/getFlowType-test.ts +++ b/src/utils/__tests__/getFlowType-test.ts @@ -1269,4 +1269,44 @@ describe('getFlowType', () => { raw: '{ subAction: SubAction }', }); }); + + it('handles ObjectTypeSpreadProperty', () => { + const typePath = statement(` + var x: {| apple: string, banana: string, ...OtherFruits |} = 2; + type OtherFruits = { orange: string } + `) + .get('declarations', 0) + .get('id') + .get('typeAnnotation') + .get('typeAnnotation'); + + expect(getFlowType(typePath, null, noopImporter)).toMatchSnapshot(); + }); + + it('handles ObjectTypeSpreadProperty from imported types', () => { + const typePath = statement(` + var x: {| apple: string, banana: string, ...MyType |} = 2; + import type { MyType } from 'MyType'; + `) + .get('declarations', 0) + .get('id') + .get('typeAnnotation') + .get('typeAnnotation'); + + expect(getFlowType(typePath, null, mockImporter)).toMatchSnapshot(); + }); + + it('handles nested ObjectTypeSpreadProperty', () => { + const typePath = statement(` + var x: {| apple: string, banana: string, ...BreakfastFruits |} = 2; + type BreakfastFruits = { mango: string, ...CitrusFruits }; + type CitrusFruits = { orange: string, lemon: string }; + `) + .get('declarations', 0) + .get('id') + .get('typeAnnotation') + .get('typeAnnotation'); + + expect(getFlowType(typePath, null, mockImporter)).toMatchSnapshot(); + }); }); diff --git a/src/utils/getFlowType.ts b/src/utils/getFlowType.ts index 243a5ddd711..2d465c93a62 100644 --- a/src/utils/getFlowType.ts +++ b/src/utils/getFlowType.ts @@ -219,6 +219,20 @@ function handleObjectTypeAnnotation( importer, ), }); + } else if (t.ObjectTypeSpreadProperty.check(param.node)) { + let spreadObject = resolveToValue(param.get('argument'), importer); + if (t.GenericTypeAnnotation.check(spreadObject.value)) { + const typeAlias = resolveToValue(spreadObject.get('id'), importer); + if (t.ObjectTypeAnnotation.check(typeAlias.get('right').value)) { + spreadObject = resolveToValue(typeAlias.get('right'), importer); + } + } + const props = handleObjectTypeAnnotation( + spreadObject, + typeParams, + importer, + ) as ObjectSignatureType; + type.signature.properties.push(...props.signature.properties); } });