Skip to content

Commit 4cf4788

Browse files
authored
Merge pull request #136 from SebastienGllmt/spread-intersection-support
Add support for spread operator when intersecting types
2 parents f90d513 + d024a8e commit 4cf4788

File tree

3 files changed

+94
-2
lines changed

3 files changed

+94
-2
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`should not insert spread when performing union of class types 1`] = `
4+
"declare class Foo {}
5+
declare class Bar {}
6+
declare var combination: Foo & Bar;
7+
"
8+
`;
9+
10+
exports[`should use spread when performing union of object types 1`] = `
11+
"declare type Foo = {
12+
foo: number,
13+
...
14+
};
15+
declare type Bar = {
16+
bar: string,
17+
...
18+
};
19+
declare var combination: { ...Foo, ...Bar };
20+
"
21+
`;
22+
23+
exports[`should use spread when performing union of object types 2`] = `
24+
"declare type Foo = {|
25+
foo: number,
26+
|};
27+
declare type Bar = {|
28+
bar: string,
29+
|};
30+
declare var combination: {| ...Foo, ...Bar |};
31+
"
32+
`;

src/__tests__/spread.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { compiler, beautify } from "..";
2+
import "../test-matchers";
3+
4+
it("should use spread when performing union of object types", () => {
5+
const ts = `
6+
type Foo = { foo: number };
7+
type Bar = { bar: string };
8+
const combination: Foo & Bar;
9+
`;
10+
11+
{
12+
const result = compiler.compileDefinitionString(ts, { quiet: true });
13+
expect(beautify(result)).toMatchSnapshot();
14+
expect(result).toBeValidFlowTypeDeclarations();
15+
}
16+
17+
{
18+
const result = compiler.compileDefinitionString(ts, {
19+
quiet: true,
20+
inexact: false,
21+
});
22+
expect(beautify(result)).toMatchSnapshot();
23+
expect(result).toBeValidFlowTypeDeclarations();
24+
}
25+
});
26+
27+
it("should not insert spread when performing union of class types", () => {
28+
const ts = `
29+
class Foo {}
30+
class Bar {}
31+
const combination: Foo & Bar;
32+
`;
33+
const result = compiler.compileDefinitionString(ts, { quiet: true });
34+
expect(beautify(result)).toMatchSnapshot();
35+
expect(result).toBeValidFlowTypeDeclarations();
36+
});

src/printers/node.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import * as logger from "../logger";
77
import { withEnv } from "../env";
88
import { renames, getLeftMostEntityName } from "./smart-identifiers";
99
import { printErrorMessage } from "../errors/error-message";
10+
import { opts } from "../options";
1011

1112
type KeywordNode =
1213
| {
@@ -748,8 +749,31 @@ export const printType = withEnv<any, [any], string>(
748749
}
749750
return "";
750751

751-
case ts.SyntaxKind.IntersectionType:
752-
return type.types.map(printType).join(" & ");
752+
case ts.SyntaxKind.IntersectionType: {
753+
// for non-class types, we can't easily just merge types together using &
754+
// this is because in Typescript
755+
// { a: number } & { b: string}
756+
// is NOT equivalent to {| a: number |} & {| b: string |} in Flow
757+
// since you can't intersect exact types in Flow
758+
// https://github.com/facebook/flow/issues/4946#issuecomment-331520118
759+
// instead, you have to use the spread notation
760+
// HOWEVER, you must use & to intersect classes (you can't spread a class)
761+
const containsClass = type.types
762+
.map(checker.current.getTypeAtLocation)
763+
.find(type => type.isClass());
764+
765+
if (containsClass) {
766+
return type.types.map(printType).join(" & ");
767+
}
768+
769+
const spreadType = type.types
770+
.map(type => `...${printType(type)}`)
771+
.join(",");
772+
773+
const isInexact = opts().inexact;
774+
775+
return isInexact ? `{ ${spreadType} }` : `{| ${spreadType} |}`;
776+
}
753777

754778
case ts.SyntaxKind.MethodDeclaration:
755779
// Skip methods marked as private

0 commit comments

Comments
 (0)