Skip to content

Commit

Permalink
Add support for spread operator when intersecting types
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienGllmt committed Apr 21, 2021
1 parent f90d513 commit 3f0622c
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 1 deletion.
32 changes: 32 additions & 0 deletions src/__tests__/__snapshots__/spread.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should not insert spread when performing union of class types 1`] = `
"declare class Foo {}
declare class Bar {}
declare var combination: Foo & Bar;
"
`;

exports[`should use spread when performing union of object types 1`] = `
"declare type Foo = {
foo: number,
...
};
declare type Bar = {
bar: string,
...
};
declare var combination: { ...Foo, ...Bar };
"
`;

exports[`should use spread when performing union of object types 2`] = `
"declare type Foo = {|
foo: number,
|};
declare type Bar = {|
bar: string,
|};
declare var combination: {| ...Foo, ...Bar |};
"
`;
34 changes: 34 additions & 0 deletions src/__tests__/spread.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { compiler, beautify } from "..";
import "../test-matchers";

it("should use spread when performing union of object types", () => {
const ts = `
type Foo = { foo: number };
type Bar = { bar: string };
const combination: Foo & Bar;
`;

{
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
}

{
const result = compiler.compileDefinitionString(ts, { quiet: true, inexact: false });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
}
});


it("should not insert spread when performing union of class types", () => {
const ts = `
class Foo {}
class Bar {}
const combination: Foo & Bar;
`;
const result = compiler.compileDefinitionString(ts, { quiet: true });
expect(beautify(result)).toMatchSnapshot();
expect(result).toBeValidFlowTypeDeclarations();
});
25 changes: 24 additions & 1 deletion src/printers/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as logger from "../logger";
import { withEnv } from "../env";
import { renames, getLeftMostEntityName } from "./smart-identifiers";
import { printErrorMessage } from "../errors/error-message";
import { opts } from "../options";

type KeywordNode =
| {
Expand Down Expand Up @@ -749,7 +750,29 @@ export const printType = withEnv<any, [any], string>(
return "";

case ts.SyntaxKind.IntersectionType:
return type.types.map(printType).join(" & ");
// for non-class types, we can't easily just merge types together using &
// this is because in Typescript
// { a: number } & { b: string}
// is NOT equivalent to {| a: number |} & {| b: string |} in Flow
// since you can't intersect exact types in Flow
// https://github.com/facebook/flow/issues/4946#issuecomment-331520118
// instead, you have to use the spread notation
// HOWEVER, you must use & to intersect classes (you can't spread a class)
const containsClass = type.types
.map(checker.current.getTypeAtLocation)
.find(type => type.isClass());

if (containsClass) {
return type.types.map(printType).join(" & ");
}

const spreadType = type.types.map(type => `...${printType(type)}`).join(",");

const isInexact = opts().inexact;

return isInexact
? `{ ${spreadType} }`
: `{| ${spreadType} |}`;

case ts.SyntaxKind.MethodDeclaration:
// Skip methods marked as private
Expand Down

0 comments on commit 3f0622c

Please sign in to comment.