From 8b2e557e41b4c05293986ec94c827934419e3fe2 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:54:56 -0700 Subject: [PATCH 01/28] printers [nfc]: Add methods printInexactObjectType and friends These will let us replace a number of separate implementations at different spots in our code of printing a Flow object type. In particular, these will always emit a form that's explicitly either exact or inexact, eliminating places where we currently emit an ambiguous form like `{ x: number }`. See Flow docs: https://flow.org/en/docs/types/objects/#toc-explicit-inexact-object-types By my count, we currently have 5 places where we emit this form and intend it to mean an inexact type, and 2 where we intend the opposite. These will also let our own code be explicit about whether it specifically intends an exact object type, or an inexact object type, or wants the exactness to be controlled by the `inexact` option. At least one of those bugs (the one affecting an `interface` with an `extends` clause) appears to be due to confusion where one bit of code is trying to specifically produce an inexact object type, but another bit that it's calling is trying to let the exactness be controlled by the `inexact` option. I suspect that sort of bug will be easier to avoid when the choice is explicit. --- src/printers/common.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/printers/common.ts b/src/printers/common.ts index c0c0e22d..865fec9a 100644 --- a/src/printers/common.ts +++ b/src/printers/common.ts @@ -22,6 +22,35 @@ export const literalType = (node: RawNode): string => { return printers.node.printType(node.type); }; +/** Print as a Flow exact object type. */ +export const printExactObjectType = (members: string[]) => { + if (members.length === 0) { + return `{||}`; + } else if (members.length === 1) { + return `{|${members[0]}|}`; + } else { + return `{|\n${members.join(",\n")}\n|}`; + } +}; + +/** Print as a Flow inexact object type. */ +export const printInexactObjectType = (members: string[]) => { + if (members.length === 0) { + return `{...}`; + } else if (members.length === 1) { + return `{${members[0]}, ...}`; + } else { + return `{\n${members.join(",\n")},\n...\n}`; + } +}; + +/** Print as a Flow object type, applying the option `inexact`. */ +export const printDefaultObjectType = (members: string[]) => { + return opts().inexact + ? printInexactObjectType(members) + : printExactObjectType(members); +}; + export const typeParameter = ( node: ts.TypeParameterDeclaration & { withoutDefault: boolean; From e865795660645df7d50399c3251d53db09d30423 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:20:02 -0700 Subject: [PATCH 02/28] printers: Fix intersections-as-spreads when `inexact` true When inexact object types are requested, we were producing the ambiguous form of a Flow object type. Depending on Flow settings (and in the future probably by default), this ends up getting interpreted as exact: https://flow.org/en/docs/types/objects/#toc-explicit-inexact-object-types --- src/__tests__/__snapshots__/spread.spec.ts.snap | 11 +++++++++-- src/printers/node.ts | 11 +++-------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/__tests__/__snapshots__/spread.spec.ts.snap b/src/__tests__/__snapshots__/spread.spec.ts.snap index d2390247..094d3008 100644 --- a/src/__tests__/__snapshots__/spread.spec.ts.snap +++ b/src/__tests__/__snapshots__/spread.spec.ts.snap @@ -16,7 +16,11 @@ declare type Bar = { bar: string, ... }; -declare var combination: { ...Foo, ...Bar }; +declare var combination: { + ...Foo, + ...Bar, + ... +}; " `; @@ -27,6 +31,9 @@ exports[`should use spread when performing union of object types 2`] = ` declare type Bar = {| bar: string, |}; -declare var combination: {| ...Foo, ...Bar |}; +declare var combination: {| + ...Foo, + ...Bar, +|}; " `; diff --git a/src/printers/node.ts b/src/printers/node.ts index de0b884d..301e0b3f 100644 --- a/src/printers/node.ts +++ b/src/printers/node.ts @@ -7,7 +7,6 @@ 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 ExpectedKeywordKind = | ts.SyntaxKind.AnyKeyword @@ -771,13 +770,9 @@ export const printType = withEnv( return type.types.map(printType).join(" & "); } - const spreadType = type.types - .map(type => `...${printType(type)}`) - .join(","); - - const isInexact = opts().inexact; - - return isInexact ? `{ ${spreadType} }` : `{| ${spreadType} |}`; + return printers.common.printDefaultObjectType( + type.types.map(type => `...${printType(type)}`), + ); } case ts.SyntaxKind.MethodDeclaration: From 3f13be14c6dd76212ed3cd0650a5547e16f7c50f Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:49:02 -0700 Subject: [PATCH 03/28] printers [nfc]: Factor out common Record and Omit logic a bit better --- src/printers/identifiers.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/printers/identifiers.ts b/src/printers/identifiers.ts index d967ceb4..98b7c2d9 100644 --- a/src/printers/identifiers.ts +++ b/src/printers/identifiers.ts @@ -5,30 +5,29 @@ import { opts } from "../options"; import { withEnv } from "../env"; import ts from "typescript"; -const Record = ([key, value]: [any, any], isInexact = opts().inexact) => { +const recordMembers = (key, value) => { const valueType = printers.node.printType(value); switch (key.kind) { case ts.SyntaxKind.LiteralType: - return `{ ${printers.node.printType(key)}: ${valueType}${ - isInexact ? ", ..." : "" - }}`; + return [`${printers.node.printType(key)}: ${valueType}`]; case ts.SyntaxKind.UnionType: if (key.types.every(t => t.kind === ts.SyntaxKind.LiteralType)) { - const fields = key.types.reduce((acc, t) => { - acc += `${printers.node.printType(t)}: ${valueType},\n`; - return acc; - }, ""); - return `{ ${fields}${isInexact ? "..." : ""}}`; + return key.types.map( + t => `${printers.node.printType(t)}: ${valueType}`, + ); } // Fallthrough default: - return `{[key: ${printers.node.printType(key)}]: ${valueType}${ - isInexact ? ", ..." : "" - }}`; + return [`[key: ${printers.node.printType(key)}]: ${valueType}`]; } }; +const Record = ([key, value]: [any, any], isInexact = opts().inexact) => { + const members = recordMembers(key, value); + return `{ ${members.join(",\n")}${isInexact ? ", ..." : ""} }`; +}; + type IdentifierResult = string | ((...args: any[]) => any); const identifiers: { [name: string]: IdentifierResult } = { From 8d2416a71928133c5c0f32ac35cf648e2280b1e5 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 14:02:11 -0700 Subject: [PATCH 04/28] printers: Fix Omit to use an explicitly exact type This needs to be an exact object type in order to correctly emulate TS's `Omit`. We were emitting the ambiguous form, which by default Flow interprets as an inexact type instead. --- src/__tests__/__snapshots__/utility-types.spec.ts.snap | 9 ++++++--- src/printers/identifiers.ts | 8 ++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/__tests__/__snapshots__/utility-types.spec.ts.snap b/src/__tests__/__snapshots__/utility-types.spec.ts.snap index b79e909a..94bd6306 100644 --- a/src/__tests__/__snapshots__/utility-types.spec.ts.snap +++ b/src/__tests__/__snapshots__/utility-types.spec.ts.snap @@ -7,7 +7,7 @@ exports[`should handle Omit type 1`] = ` b: number, ... }, - { a: any } + {| a: any |} >; declare type B = $Diff< { @@ -15,7 +15,10 @@ declare type B = $Diff< b: number, ... }, - { a: any, b: any } + {| + a: any, + b: any, + |} >; declare type O = { a: string, @@ -23,7 +26,7 @@ declare type O = { ... }; declare type U = \\"a\\"; -declare type C = $Diff; +declare type C = $Diff; " `; diff --git a/src/printers/identifiers.ts b/src/printers/identifiers.ts index 98b7c2d9..0fdb4b41 100644 --- a/src/printers/identifiers.ts +++ b/src/printers/identifiers.ts @@ -50,10 +50,10 @@ const identifiers: { [name: string]: IdentifierResult } = { }, Record, Omit: ([obj, keys]: [any, any]) => { - return `$Diff<${printers.node.printType(obj)},${Record( - [keys, { kind: ts.SyntaxKind.AnyKeyword }], - false, - )}>`; + const members = recordMembers(keys, { kind: ts.SyntaxKind.AnyKeyword }); + return `$Diff<${printers.node.printType( + obj, + )},${printers.common.printExactObjectType(members)}>`; }, }; From c33cc5b4c5b0a511d31b2a2f4d7e6802a1a7fd7a Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 14:02:11 -0700 Subject: [PATCH 05/28] printers: Fix Record when `inexact` false, to use explicit exact types Otherwise we accidentally get an inexact type after all, in Flow's default configuration. Also add a test. --- .../__snapshots__/utility-types.spec.ts.snap | 31 +++++++++++++++++++ src/__tests__/utility-types.spec.ts | 7 +++++ src/printers/identifiers.ts | 10 +++--- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/src/__tests__/__snapshots__/utility-types.spec.ts.snap b/src/__tests__/__snapshots__/utility-types.spec.ts.snap index 94bd6306..5137b4f9 100644 --- a/src/__tests__/__snapshots__/utility-types.spec.ts.snap +++ b/src/__tests__/__snapshots__/utility-types.spec.ts.snap @@ -62,3 +62,34 @@ declare type E2 = $Call<((...args: any[]) => R) => R, () => T>; declare type F2 = { [key: T]: U, ... }; " `; + +exports[`should handle utility types 2`] = ` +"declare type A = $ReadOnly<{| + a: number, +|}>; +declare type B = $Rest< + {| + a: number, + |}, + {} +>; +declare type C = $NonMaybeType; +declare type D = $ReadOnlyArray; +declare type E = $Call<((...args: any[]) => R) => R, () => string>; +declare type F = {| [key: string]: number |}; +declare type G = $ReadOnlySet; +declare type H = $ReadOnlyMap; +declare type A1 = Readonly; +declare type B1 = Partial; +declare type C1 = NonNullable; +declare type D1 = ReadonlyArray; +declare type E1 = ReturnType; +declare type F1 = Record; +declare type A2 = $ReadOnly; +declare type B2 = $Rest; +declare type C2 = $NonMaybeType; +declare type D2 = $ReadOnlyArray; +declare type E2 = $Call<((...args: any[]) => R) => R, () => T>; +declare type F2 = {| [key: T]: U |}; +" +`; diff --git a/src/__tests__/utility-types.spec.ts b/src/__tests__/utility-types.spec.ts index c275f599..fa17f299 100644 --- a/src/__tests__/utility-types.spec.ts +++ b/src/__tests__/utility-types.spec.ts @@ -29,6 +29,13 @@ type F2 = Record const result = compiler.compileDefinitionString(ts, { quiet: true }); expect(beautify(result)).toMatchSnapshot(); expect(result).toBeValidFlowTypeDeclarations(); + + const result2 = compiler.compileDefinitionString(ts, { + quiet: true, + inexact: false, + }); + expect(beautify(result2)).toMatchSnapshot(); + expect(result2).toBeValidFlowTypeDeclarations(); }); it("should handle Omit type", () => { diff --git a/src/printers/identifiers.ts b/src/printers/identifiers.ts index 0fdb4b41..bb90dbfe 100644 --- a/src/printers/identifiers.ts +++ b/src/printers/identifiers.ts @@ -23,11 +23,6 @@ const recordMembers = (key, value) => { } }; -const Record = ([key, value]: [any, any], isInexact = opts().inexact) => { - const members = recordMembers(key, value); - return `{ ${members.join(",\n")}${isInexact ? ", ..." : ""} }`; -}; - type IdentifierResult = string | ((...args: any[]) => any); const identifiers: { [name: string]: IdentifierResult } = { @@ -48,7 +43,10 @@ const identifiers: { [name: string]: IdentifierResult } = { typeArguments[0], )}>`; }, - Record, + Record: ([keys, value]: [any, any]) => { + const members = recordMembers(keys, value); + return printers.common.printDefaultObjectType(members); + }, Omit: ([obj, keys]: [any, any]) => { const members = recordMembers(keys, { kind: ts.SyntaxKind.AnyKeyword }); return `$Diff<${printers.node.printType( From 3f1e06df118480980a3708a8e1cd7ecf7c303792 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:26:24 -0700 Subject: [PATCH 06/28] printers: Fix Partial when `inexact` false, to use explicit inexact type This second argument to `$Rest` needs to be the inexact empty object type (`{ ... }`), not the exact empty object type, in order for this to do its job of implementing the equivalent of TS's `Partial`. --- src/__tests__/__snapshots__/utility-types.spec.ts.snap | 4 ++-- src/printers/identifiers.ts | 8 +++----- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/__tests__/__snapshots__/utility-types.spec.ts.snap b/src/__tests__/__snapshots__/utility-types.spec.ts.snap index 5137b4f9..8a35786f 100644 --- a/src/__tests__/__snapshots__/utility-types.spec.ts.snap +++ b/src/__tests__/__snapshots__/utility-types.spec.ts.snap @@ -71,7 +71,7 @@ declare type B = $Rest< {| a: number, |}, - {} + { ... } >; declare type C = $NonMaybeType; declare type D = $ReadOnlyArray; @@ -86,7 +86,7 @@ declare type D1 = ReadonlyArray; declare type E1 = ReturnType; declare type F1 = Record; declare type A2 = $ReadOnly; -declare type B2 = $Rest; +declare type B2 = $Rest; declare type C2 = $NonMaybeType; declare type D2 = $ReadOnlyArray; declare type E2 = $Call<((...args: any[]) => R) => R, () => T>; diff --git a/src/printers/identifiers.ts b/src/printers/identifiers.ts index bb90dbfe..f11ae48a 100644 --- a/src/printers/identifiers.ts +++ b/src/printers/identifiers.ts @@ -1,7 +1,6 @@ // Please add only built-in type references import * as printers from "./index"; -import { opts } from "../options"; import { withEnv } from "../env"; import ts from "typescript"; @@ -33,10 +32,9 @@ const identifiers: { [name: string]: IdentifierResult } = { RegExpMatchArray: "RegExp$matchResult", NonNullable: "$NonMaybeType", Partial: ([type]: any[]) => { - const isInexact = opts().inexact; - return `$Rest<${printers.node.printType(type)}, {${ - isInexact ? "..." : "" - }}>`; + return `$Rest<${printers.node.printType( + type, + )}, ${printers.common.printInexactObjectType([])}>`; }, ReturnType: (typeArguments: any[]) => { return `$Call<((...args: any[]) => R) => R, ${printers.node.printType( From ca79e4dc4924ef5ef1ee1af5cd2818690c41e163 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 13:51:24 -0700 Subject: [PATCH 07/28] printers: Fix untyped object binding patterns to use explicit inexact types These types should always be inexact object types. We were producing the ambiguous form, which can end up meaning an exact object type: https://flow.org/en/docs/types/objects/#toc-explicit-inexact-object-types --- src/__tests__/__snapshots__/interfaces.spec.ts.snap | 8 ++++++-- src/printers/common.ts | 12 +++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/__tests__/__snapshots__/interfaces.spec.ts.snap b/src/__tests__/__snapshots__/interfaces.spec.ts.snap index a2c3e5be..6d573cfc 100644 --- a/src/__tests__/__snapshots__/interfaces.spec.ts.snap +++ b/src/__tests__/__snapshots__/interfaces.spec.ts.snap @@ -133,8 +133,12 @@ exports[`should handle untyped array binding pattern 1`] = ` exports[`should handle untyped object binding pattern 1`] = ` "declare interface ObjectBinding { (): void; - (x: {}): void; - (x: { a: any, b: any }): void; + (x: { ... }): void; + (x: { + a: any, + b: any, + ... + }): void; } " `; diff --git a/src/printers/common.ts b/src/printers/common.ts index 865fec9a..4d52ce8f 100644 --- a/src/printers/common.ts +++ b/src/printers/common.ts @@ -96,13 +96,11 @@ export const parameter = ( if (!param.type) { if (param.name.kind === ts.SyntaxKind.ObjectBindingPattern) { - if (param.name.elements.length > 0) { - right = `{${param.name.elements - .map(element => `${printers.node.printType(element)}: any`) - .join(", ")}}`; - } else { - right = "{}"; - } + right = printInexactObjectType( + param.name.elements.map( + element => `${printers.node.printType(element)}: any`, + ), + ); } else { right = "any"; } From fe6d723acc1e0e3d85f94753c105f3b8fbf82aa3 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:23:18 -0700 Subject: [PATCH 08/28] printers: Use common printExactObjectType for namespaces This produces exactly the same type, but using our common printer function for it. Some snapshots change just because of formatting. --- .../__snapshots__/namespaces.spec.ts.snap | 64 +++++-------------- src/nodes/namespace.ts | 6 +- 2 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/__tests__/__snapshots__/namespaces.spec.ts.snap b/src/__tests__/__snapshots__/namespaces.spec.ts.snap index 02ca7d8c..647d9368 100644 --- a/src/__tests__/__snapshots__/namespaces.spec.ts.snap +++ b/src/__tests__/__snapshots__/namespaces.spec.ts.snap @@ -35,9 +35,7 @@ exports[`should handle merging with other types enum 1`] = ` |}; declare var Color: typeof npm$namespace$Color; -declare var npm$namespace$Color: {| - mixColor: typeof Color$mixColor, -|}; +declare var npm$namespace$Color: {| mixColor: typeof Color$mixColor |}; declare export function Color$mixColor(colorName: string): number; " `; @@ -69,9 +67,7 @@ export interface test$Foo { exports[`should handle merging with other types function type 1`] = ` "declare var test: typeof npm$namespace$test; -declare var npm$namespace$test: {| - (foo: number): string, -|}; +declare var npm$namespace$test: {| (foo: number): string |}; export type test$Foo = { bar: number, ... @@ -82,9 +78,7 @@ export type test$Foo = { exports[`should handle namespace function merging 1`] = ` "declare var test: typeof npm$namespace$test; -declare var npm$namespace$test: {| - test: typeof test$test, -|}; +declare var npm$namespace$test: {| test: typeof test$test |}; declare function test$test(err: number): void; declare function test$test(response: string): string; @@ -107,9 +101,7 @@ declare export var test$error: string; exports[`should handle namespaces 1`] = ` "declare var test: typeof npm$namespace$test; -declare var npm$namespace$test: {| - ok: typeof test$ok, -|}; +declare var npm$namespace$test: {| ok: typeof test$ok |}; declare export var test$ok: number; " `; @@ -117,9 +109,7 @@ declare export var test$ok: number; exports[`should handle nested namespace merging class 1`] = ` "declare var ns: typeof npm$namespace$ns; -declare var npm$namespace$ns: {| - Album: typeof ns$Album, -|}; +declare var npm$namespace$ns: {| Album: typeof ns$Album |}; declare class ns$Album { label: ns$Album.AlbumLabel; static AlbumLabel: typeof ns$Album$AlbumLabel; @@ -132,9 +122,7 @@ declare export class ns$Album$AlbumLabel {} exports[`should handle nested namespace merging enum 1`] = ` "declare var ns: typeof npm$namespace$ns; -declare var npm$namespace$ns: {| - Color: typeof npm$namespace$ns$Color, -|}; +declare var npm$namespace$ns: {| Color: typeof npm$namespace$ns$Color |}; declare var ns$Color: {| +red: 1, // 1 @@ -142,9 +130,7 @@ declare var ns$Color: {| +blue: 4, // 4 |}; -declare var npm$namespace$ns$Color: {| - mixColor: typeof ns$Color$mixColor, -|}; +declare var npm$namespace$ns$Color: {| mixColor: typeof ns$Color$mixColor |}; declare export function ns$Color$mixColor(colorName: string): number; " `; @@ -152,9 +138,7 @@ declare export function ns$Color$mixColor(colorName: string): number; exports[`should handle nested namespace merging function const 1`] = ` "declare var ns: typeof npm$namespace$ns; -declare var npm$namespace$ns: {| - test: typeof npm$namespace$ns$test, -|}; +declare var npm$namespace$ns: {| test: typeof npm$namespace$ns$test |}; declare var npm$namespace$ns$test: {| (foo: number): string, @@ -167,9 +151,7 @@ declare export var test$ok: number; exports[`should handle nested namespace merging function interface 1`] = ` "declare var ns: typeof npm$namespace$ns; -declare var npm$namespace$ns: {| - test: typeof ns$test, -|}; +declare var npm$namespace$ns: {| test: typeof ns$test |}; declare var npm$namespace$ns$test: {| (foo: number): string, @@ -184,13 +166,9 @@ export interface ns$test$Foo { exports[`should handle nested namespace merging function type 1`] = ` "declare var ns: typeof npm$namespace$ns; -declare var npm$namespace$ns: {| - test: typeof ns$test, -|}; +declare var npm$namespace$ns: {| test: typeof ns$test |}; -declare var npm$namespace$ns$test: {| - (foo: number): string, -|}; +declare var npm$namespace$ns$test: {| (foo: number): string |}; export type ns$test$Foo = { bar: number, ... @@ -226,9 +204,7 @@ declare var E0$U1$E2: {| +E: 1, // 1 |}; -declare var npm$namespace$E0$U1$D1: {| - S2: typeof npm$namespace$E0$U1$D1$S2, -|}; +declare var npm$namespace$E0$U1$D1: {| S2: typeof npm$namespace$E0$U1$D1$S2 |}; declare var npm$namespace$E0$U1$D1$S2: {| n3: typeof E0$U1$D1$S2$n3, @@ -243,16 +219,12 @@ declare var E0$U1$D1$S2$n3: Symbol; declare class E0$U1$D1$S2$N3 {} -declare var npm$namespace$E0$U1$DD1$S2: {| - S3: Class, -|}; +declare var npm$namespace$E0$U1$DD1$S2: {| S3: Class |}; declare interface E0$U1$DD1$S2$S3 { e: number; } -declare var npm$namespace$E0$S1: {| - m3: typeof E0$S1$m3, -|}; +declare var npm$namespace$E0$S1: {| m3: typeof E0$S1$m3 |}; declare var E0$S1$m3: string; declare var E0$s1: string; @@ -262,9 +234,7 @@ declare var E0$s1: string; exports[`should handle qualified namespaces 1`] = ` "declare var A: typeof npm$namespace$A; -declare var npm$namespace$A: {| - B: typeof npm$namespace$A$B, -|}; +declare var npm$namespace$A: {| B: typeof npm$namespace$A$B |}; declare var npm$namespace$A$B: {| D: typeof A$B$D, @@ -277,9 +247,7 @@ declare interface A$B$S { declare class A$B$D {} -declare var npm$namespace$A$B$C: {| - N: typeof A$B$C$N, -|}; +declare var npm$namespace$A$B$C: {| N: typeof A$B$C$N |}; declare class A$B$C$N mixins A$B$D, A$B$S { a: string; } diff --git a/src/nodes/namespace.ts b/src/nodes/namespace.ts index bc7bb4f7..6bd28337 100644 --- a/src/nodes/namespace.ts +++ b/src/nodes/namespace.ts @@ -101,9 +101,9 @@ export default class Namespace extends Node { if (childrenDeclarations.length > 0) { let topLevel = ""; const nsGroup = ` - declare var npm$namespace$${name}: {| - ${childrenDeclarations.map(declaration => `${declaration},`).join("\n")} - |}\n`; + declare var npm$namespace$${name}: ${printers.common.printExactObjectType( + childrenDeclarations, + )}\n`; if (namespace === "") { topLevel = `declare var ${name}: typeof npm$namespace$${name};\n`; } From 740f24fa80b79009ebf1cf5a0b5df98f455bd485 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 10:49:27 -0700 Subject: [PATCH 09/28] printers [nfc]: Slightly simplify loop logic in interfaceType The only cases this `filter` can filter out (or is meant to) are those from this `map` up at the top. So put it directly there. --- src/printers/declarations.ts | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 6bc2eb14..6e32337b 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -67,13 +67,15 @@ export const interfaceType = ( isType = false, ): string => { const isInexact = opts().inexact; - const members = node.members.map(member => { - const printed = printers.node.printType(member); - if (!printed) { - return null; - } - return "\n" + printers.common.jsdoc(member) + printed; - }); + const members = node.members + .map(member => { + const printed = printers.node.printType(member); + if (!printed) { + return null; + } + return "\n" + printers.common.jsdoc(member) + printed; + }) + .filter(Boolean); // Filter rows which didn't print properly (private fields et al) if (mergedNamespaceChildren.length > 0) { for (const child of Namespace.formatChildren( @@ -90,9 +92,7 @@ export const interfaceType = ( members.push("\n"); } - const inner = members - .filter(Boolean) // Filter rows which didn't print properly (private fields et al) - .join(withSemicolons ? ";" : ","); + const inner = members.join(withSemicolons ? ";" : ","); // we only want type literals to be exact. i.e. class Foo {} should not be class Foo {||} if (!ts.isTypeLiteralNode(node)) { From 324fe97ea35beb77794d8c05ed77621517374a2b Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 12:31:05 -0700 Subject: [PATCH 10/28] printers [nfc]: Factor out helper typeMembers, deduplicating --- src/printers/declarations.ts | 37 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 6e32337b..1f2ab0c5 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -59,15 +59,13 @@ export const variableDeclaration = (node: ts.VariableStatement): string => { .join("\n"); }; -export const interfaceType = ( +/** + * The members of the type, printed with their jsdoc. + */ +export const typeMembers = ( node: ts.InterfaceDeclaration | ts.ClassDeclaration | ts.TypeLiteralNode, - nodeName: string, - mergedNamespaceChildren: ReadonlyArray>, - withSemicolons = false, - isType = false, -): string => { - const isInexact = opts().inexact; - const members = node.members +): string[] => { + return node.members .map(member => { const printed = printers.node.printType(member); if (!printed) { @@ -76,6 +74,18 @@ export const interfaceType = ( return "\n" + printers.common.jsdoc(member) + printed; }) .filter(Boolean); // Filter rows which didn't print properly (private fields et al) +}; + +export const interfaceType = ( + node: ts.InterfaceDeclaration | ts.ClassDeclaration | ts.TypeLiteralNode, + nodeName: string, + mergedNamespaceChildren: ReadonlyArray>, + withSemicolons = false, + isType = false, +): string => { + const isInexact = opts().inexact; + + const members = typeMembers(node); if (mergedNamespaceChildren.length > 0) { for (const child of Namespace.formatChildren( @@ -107,16 +117,7 @@ const interfaceRecordType = ( withSemicolons = false, ): string => { const isInexact = opts().inexact; - let members = node.members - .map(member => { - const printed = printers.node.printType(member); - if (!printed) { - return null; - } - return "\n" + printers.common.jsdoc(member) + printed; - }) - .filter(Boolean) // Filter rows which didnt print propely (private fields et al) - .join(withSemicolons ? ";" : ","); + let members = typeMembers(node).join(withSemicolons ? ";" : ","); if (members.length > 0) { members += "\n"; From 176e3ceb4e6b24ef364b6698f9fbdda2af068f45 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 12:01:05 -0700 Subject: [PATCH 11/28] printers [nfc]: Cut unused withSemicolons on interfaceRecordType This has just one call site, and it doesn't use this flag. --- src/printers/declarations.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 1f2ab0c5..7e7aa8d2 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -114,10 +114,9 @@ export const interfaceType = ( const interfaceRecordType = ( node: ts.InterfaceDeclaration, heritage: string, - withSemicolons = false, ): string => { const isInexact = opts().inexact; - let members = typeMembers(node).join(withSemicolons ? ";" : ","); + let members = typeMembers(node).join(","); if (members.length > 0) { members += "\n"; From fad4e4b149c02951ef14d7b42dc03da15870003b Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 12:39:29 -0700 Subject: [PATCH 12/28] printers [nfc]: Split classBody from interfaceType This function has three call sites. One of them has needs that are fairly divergent from the needs of the other two. So, split the function in two, and make both sides simpler. --- src/printers/declarations.ts | 51 ++++++++++++++++++++---------------- src/printers/node.ts | 2 +- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 7e7aa8d2..de63291d 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -76,17 +76,36 @@ export const typeMembers = ( .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; -export const interfaceType = ( - node: ts.InterfaceDeclaration | ts.ClassDeclaration | ts.TypeLiteralNode, - nodeName: string, - mergedNamespaceChildren: ReadonlyArray>, - withSemicolons = false, +export const interfaceType = ( + node: ts.InterfaceDeclaration | ts.TypeLiteralNode, isType = false, ): string => { const isInexact = opts().inexact; const members = typeMembers(node); + if (isType && isInexact) { + members.push("...\n"); + } else if (members.length > 0) { + members.push("\n"); + } + + const inner = members.join(","); + + // we only want type literals to be exact. i.e. class Foo {} should not be class Foo {||} + if (!ts.isTypeLiteralNode(node)) { + return `{${inner}}`; + } + return isInexact ? `{${inner}}` : `{|${inner}|}`; +}; + +const classBody = ( + node: ts.ClassDeclaration, + nodeName: string, + mergedNamespaceChildren: ReadonlyArray>, +): string => { + const members = typeMembers(node); + if (mergedNamespaceChildren.length > 0) { for (const child of Namespace.formatChildren( mergedNamespaceChildren, @@ -96,19 +115,13 @@ export const interfaceType = ( } } - if (isType && isInexact) { - members.push("...\n"); - } else if (members.length > 0) { + if (members.length > 0) { members.push("\n"); } - const inner = members.join(withSemicolons ? ";" : ","); + const inner = members.join(";"); - // we only want type literals to be exact. i.e. class Foo {} should not be class Foo {||} - if (!ts.isTypeLiteralNode(node)) { - return `{${inner}}`; - } - return isInexact ? `{${inner}}` : `{|${inner}|}`; + return `{${inner}}`; }; const interfaceRecordType = ( @@ -228,9 +241,6 @@ export const interfaceDeclaration = ( node.typeParameters, )} ${type === "type" ? "= " : ""}${interfaceType( node, - nodeName, - [], - false, type === "type", )} ${heritage}`; @@ -317,12 +327,7 @@ export const classDeclaration = ( node, )}class ${nodeName}${printers.common.generics( node.typeParameters, - )} ${heritage} ${interfaceType( - node, - nodeName, - mergedNamespaceChildren, - true, - )}`; + )} ${heritage} ${classBody(node, nodeName, mergedNamespaceChildren)}`; return str; }; diff --git a/src/printers/node.ts b/src/printers/node.ts index 301e0b3f..b7f9ee6f 100644 --- a/src/printers/node.ts +++ b/src/printers/node.ts @@ -498,7 +498,7 @@ export const printType = withEnv( return printers.functions.functionType(type); case ts.SyntaxKind.TypeLiteral: - return printers.declarations.interfaceType(type, "", [], false, true); + return printers.declarations.interfaceType(type, true); //case SyntaxKind.IdentifierObject: //case SyntaxKind.StringLiteralType: From 6dbdd5123ca306da27494ae92b4f2a505c6d490b Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 13:01:48 -0700 Subject: [PATCH 13/28] printers [nfc]: Split interfaceDeclaration logic for type vs. interface The code that gets copied here is quite short, and we can immediately make both copies simpler and easier to read than when there was one copy trying to do both things. --- src/printers/declarations.ts | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index de63291d..04a739f3 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -223,28 +223,24 @@ export const interfaceDeclaration = ( if (isRecord) { return interfaceRecordDeclaration(nodeName, node, modifier); } - let heritage = ""; - // If the class is extending something if (node.heritageClauses) { - heritage = node.heritageClauses + // The interface is extending something. + let heritage = node.heritageClauses .map(clause => { return clause.types.map(interfaceHeritageClause).join(" & "); }) .join(""); heritage = heritage.length > 0 ? `& ${heritage}\n` : ""; - } - - const type = node.heritageClauses ? "type" : "interface"; - const str = `${modifier}${type} ${nodeName}${printers.common.generics( - node.typeParameters, - )} ${type === "type" ? "= " : ""}${interfaceType( - node, - type === "type", - )} ${heritage}`; - - return str; + return `${modifier}type ${nodeName}${printers.common.generics( + node.typeParameters, + )} = ${interfaceType(node, true)} ${heritage}`; + } else { + return `${modifier}interface ${nodeName}${printers.common.generics( + node.typeParameters, + )} ${interfaceType(node, false)} `; + } }; export const typeDeclaration = ( From 37ce1900afd0a1e7628c53fb88958aa1fafb4023 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 13:08:56 -0700 Subject: [PATCH 14/28] printers [nfc]: Split objectType from interfaceTypeBody This makes it possible to cleanly describe what each of these functions should do. Now that one of them is always printing a Flow object type, we'll shortly be able to replace it with calls to the common printers for object types. --- src/printers/declarations.ts | 22 +++++++++++++++++----- src/printers/node.ts | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 04a739f3..d9695008 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -76,15 +76,15 @@ export const typeMembers = ( .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; -export const interfaceType = ( +/** Print as a Flow object type. */ +export const objectType = ( node: ts.InterfaceDeclaration | ts.TypeLiteralNode, - isType = false, ): string => { const isInexact = opts().inexact; const members = typeMembers(node); - if (isType && isInexact) { + if (isInexact) { members.push("...\n"); } else if (members.length > 0) { members.push("\n"); @@ -99,6 +99,18 @@ export const interfaceType = ( return isInexact ? `{${inner}}` : `{|${inner}|}`; }; +/** Print as a Flow interface type's body (the `{…}` portion.) */ +const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { + const members = typeMembers(node); + if (members.length > 0) { + members.push("\n"); + } + + const inner = members.join(","); + + return `{${inner}}`; +}; + const classBody = ( node: ts.ClassDeclaration, nodeName: string, @@ -235,11 +247,11 @@ export const interfaceDeclaration = ( return `${modifier}type ${nodeName}${printers.common.generics( node.typeParameters, - )} = ${interfaceType(node, true)} ${heritage}`; + )} = ${objectType(node)} ${heritage}`; } else { return `${modifier}interface ${nodeName}${printers.common.generics( node.typeParameters, - )} ${interfaceType(node, false)} `; + )} ${interfaceTypeBody(node)} `; } }; diff --git a/src/printers/node.ts b/src/printers/node.ts index b7f9ee6f..378b61df 100644 --- a/src/printers/node.ts +++ b/src/printers/node.ts @@ -498,7 +498,7 @@ export const printType = withEnv( return printers.functions.functionType(type); case ts.SyntaxKind.TypeLiteral: - return printers.declarations.interfaceType(type, true); + return printers.declarations.objectType(type); //case SyntaxKind.IdentifierObject: //case SyntaxKind.StringLiteralType: From 0f64e4addc85b449fd8a62696860c693a8fe593b Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 17:19:35 -0700 Subject: [PATCH 15/28] printers [nfc]: Push control of newlines out of typeMembers, to callers Those `.map` calls will mostly go away as we convert these call sites to use `printers.common.print*Object` to combine the members. --- src/printers/declarations.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index d9695008..6429c81a 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -71,7 +71,7 @@ export const typeMembers = ( if (!printed) { return null; } - return "\n" + printers.common.jsdoc(member) + printed; + return printers.common.jsdoc(member) + printed; }) .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; @@ -82,7 +82,7 @@ export const objectType = ( ): string => { const isInexact = opts().inexact; - const members = typeMembers(node); + const members = typeMembers(node).map(m => `\n${m}`); if (isInexact) { members.push("...\n"); @@ -101,7 +101,7 @@ export const objectType = ( /** Print as a Flow interface type's body (the `{…}` portion.) */ const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { - const members = typeMembers(node); + const members = typeMembers(node).map(m => `\n${m}`); if (members.length > 0) { members.push("\n"); } @@ -116,7 +116,7 @@ const classBody = ( nodeName: string, mergedNamespaceChildren: ReadonlyArray>, ): string => { - const members = typeMembers(node); + const members = typeMembers(node).map(m => `\n${m}`); if (mergedNamespaceChildren.length > 0) { for (const child of Namespace.formatChildren( @@ -141,7 +141,9 @@ const interfaceRecordType = ( heritage: string, ): string => { const isInexact = opts().inexact; - let members = typeMembers(node).join(","); + let members = typeMembers(node) + .map(m => `\n${m}`) + .join(","); if (members.length > 0) { members += "\n"; From 3e8963c104e65d4398507b4236f60159c741c97c Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 13:15:23 -0700 Subject: [PATCH 16/28] printers [nfc]: Make interfaceDeclaration's inexact-forcing explicit This will help make it clearer how to replace the two calls to this `objectType` method with calls to the common `print*ObjectType`. --- src/printers/declarations.ts | 16 +++++++++------- src/printers/node.ts | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 6429c81a..318202c5 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -76,9 +76,15 @@ export const typeMembers = ( .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; -/** Print as a Flow object type. */ +/** + * Print as a Flow object type. + * + * If `forceInexact`, then print as an inexact object type. Otherwise, the + * exactness will be governed by the `inexact` option. + */ export const objectType = ( node: ts.InterfaceDeclaration | ts.TypeLiteralNode, + forceInexact: boolean, ): string => { const isInexact = opts().inexact; @@ -92,11 +98,7 @@ export const objectType = ( const inner = members.join(","); - // we only want type literals to be exact. i.e. class Foo {} should not be class Foo {||} - if (!ts.isTypeLiteralNode(node)) { - return `{${inner}}`; - } - return isInexact ? `{${inner}}` : `{|${inner}|}`; + return isInexact || forceInexact ? `{${inner}}` : `{|${inner}|}`; }; /** Print as a Flow interface type's body (the `{…}` portion.) */ @@ -249,7 +251,7 @@ export const interfaceDeclaration = ( return `${modifier}type ${nodeName}${printers.common.generics( node.typeParameters, - )} = ${objectType(node)} ${heritage}`; + )} = ${objectType(node, true /* inexact so `&` works */)} ${heritage}`; } else { return `${modifier}interface ${nodeName}${printers.common.generics( node.typeParameters, diff --git a/src/printers/node.ts b/src/printers/node.ts index 378b61df..534c883c 100644 --- a/src/printers/node.ts +++ b/src/printers/node.ts @@ -498,7 +498,7 @@ export const printType = withEnv( return printers.functions.functionType(type); case ts.SyntaxKind.TypeLiteral: - return printers.declarations.objectType(type); + return printers.declarations.objectType(type, false); //case SyntaxKind.IdentifierObject: //case SyntaxKind.StringLiteralType: From ef5ce3ed6ca3e6de5d0e66fb42dbaa1e01031299 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:39:36 -0700 Subject: [PATCH 17/28] printers: Use common printDefaultObjectType for type literals This produces exactly the same types: the existing code was already successfully producing an explicit inexact object type when the `inexact` option is true, and an explicit exact object type when that option is false. The formatting in the case of a type with a single member is nicely more compact, though, and some snapshots change as a result. --- .../__snapshots__/modules.spec.ts.snap | 10 ++------ .../__snapshots__/type-exports.spec.ts.snap | 25 +++---------------- src/printers/node.ts | 4 ++- 3 files changed, 8 insertions(+), 31 deletions(-) diff --git a/src/__tests__/__snapshots__/modules.spec.ts.snap b/src/__tests__/__snapshots__/modules.spec.ts.snap index 9526e429..73643757 100644 --- a/src/__tests__/__snapshots__/modules.spec.ts.snap +++ b/src/__tests__/__snapshots__/modules.spec.ts.snap @@ -10,14 +10,8 @@ exports[`should handle module 1`] = ` value: T, ... } - | { - type: \\"nothing\\", - ... - }; - declare export type Ref = { - current: T, - ... - }; + | { type: \\"nothing\\", ... }; + declare export type Ref = { current: T, ... }; declare export var ok: number; } " diff --git a/src/__tests__/__snapshots__/type-exports.spec.ts.snap b/src/__tests__/__snapshots__/type-exports.spec.ts.snap index 87682b52..3c40b9e4 100644 --- a/src/__tests__/__snapshots__/type-exports.spec.ts.snap +++ b/src/__tests__/__snapshots__/type-exports.spec.ts.snap @@ -1,15 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should handle export list syntax 1`] = ` -"declare type ComplexType = - | { - type: number, - ... - } - | { - type: string, - ... - }; +"declare type ComplexType = { type: number, ... } | { type: string, ... }; export type { ComplexType }; declare var foo: 5; declare export { foo }; @@ -24,23 +16,12 @@ export type Maybe = value: T, ... } - | { - type: \\"nothing\\", - ... - }; + | { type: \\"nothing\\", ... }; " `; exports[`should handle inline export list syntax 1`] = ` -"declare type ComplexType = - | { - type: number, - ... - } - | { - type: string, - ... - }; +"declare type ComplexType = { type: number, ... } | { type: string, ... }; declare var foo: 5; export type { ComplexType }; declare export { foo }; diff --git a/src/printers/node.ts b/src/printers/node.ts index 534c883c..4bf483df 100644 --- a/src/printers/node.ts +++ b/src/printers/node.ts @@ -498,7 +498,9 @@ export const printType = withEnv( return printers.functions.functionType(type); case ts.SyntaxKind.TypeLiteral: - return printers.declarations.objectType(type, false); + return printers.common.printDefaultObjectType( + printers.declarations.typeMembers(type), + ); //case SyntaxKind.IdentifierObject: //case SyntaxKind.StringLiteralType: From d8e8e9e7b59a68f617b652154523bf1132ef2952 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 13:18:08 -0700 Subject: [PATCH 18/28] printers: Fix interface-extends to stay explicitly inexact when `inexact` false These object types must be inexact in order for the intersection to have the intended effect. When the `inexact` option was false, this logic would end up adjusting part of the syntax as if we wanted an exact object type. The net result was an object type that was ambiguously exact or inexact, with no `{|` and `|}` but no `...` either. A bonus is that when the interface has just one member of its own, the formatting is nicely more compact. This causes a number of snapshots to update. Also add a test, with `inexact: false`. --- .../__snapshots__/classes.spec.ts.snap | 5 +--- .../duplicated-names.spec.ts.snap | 5 +--- .../__snapshots__/interfaces.spec.ts.snap | 19 ++++++------ .../__snapshots__/mapped-types.spec.ts.snap | 5 +--- .../module-identifiers.spec.ts.snap | 5 +--- .../__snapshots__/namespaces.spec.ts.snap | 10 ++----- .../__snapshots__/spread.spec.ts.snap | 18 +++-------- .../string-literals.spec.ts.snap | 24 ++------------- .../__snapshots__/utility-types.spec.ts.snap | 24 +++------------ .../__snapshots__/value-exports.spec.ts.snap | 10 ++----- .../__snapshots__/variables.spec.ts.snap | 5 +--- src/__tests__/interfaces.spec.ts | 6 ++++ src/printers/declarations.ts | 30 +++---------------- 13 files changed, 40 insertions(+), 126 deletions(-) diff --git a/src/__tests__/__snapshots__/classes.spec.ts.snap b/src/__tests__/__snapshots__/classes.spec.ts.snap index f647b665..095981bb 100644 --- a/src/__tests__/__snapshots__/classes.spec.ts.snap +++ b/src/__tests__/__snapshots__/classes.spec.ts.snap @@ -17,10 +17,7 @@ declare class Observable mixins Subscribable { jump?: () => void; +jump?: () => void; static +jump?: () => void; - cfnProperties: { - [key: string]: any, - ... - }; + cfnProperties: { [key: string]: any, ... }; static fooGet: string; } " diff --git a/src/__tests__/__snapshots__/duplicated-names.spec.ts.snap b/src/__tests__/__snapshots__/duplicated-names.spec.ts.snap index 8b1c0a57..fe66121b 100644 --- a/src/__tests__/__snapshots__/duplicated-names.spec.ts.snap +++ b/src/__tests__/__snapshots__/duplicated-names.spec.ts.snap @@ -34,10 +34,7 @@ export type AuthMechanismType = $ElementType< `; exports[`should support generic type rename 1`] = ` -"declare export var ProfilingLevel: $ReadOnly<{ - +off: \\"off\\", - ... -}>; +"declare export var ProfilingLevel: $ReadOnly<{ +off: \\"off\\", ... }>; export type ProfilingLevelType = $ElementType< typeof ProfilingLevel, $Keys diff --git a/src/__tests__/__snapshots__/interfaces.spec.ts.snap b/src/__tests__/__snapshots__/interfaces.spec.ts.snap index 6d573cfc..d4a10015 100644 --- a/src/__tests__/__snapshots__/interfaces.spec.ts.snap +++ b/src/__tests__/__snapshots__/interfaces.spec.ts.snap @@ -14,10 +14,7 @@ exports[`should handle interface inheritance 1`] = ` "declare interface User { firstName: string; } -declare type SpecialUser = { - nice: number, - ... -} & User; +declare type SpecialUser = { nice: number, ... } & User; " `; @@ -45,6 +42,14 @@ declare type SpecialUser = {| " `; +exports[`should handle interface inheritance 4`] = ` +"declare interface User { + firstName: string; +} +declare type SpecialUser = { nice: number, ... } & User; +" +`; + exports[`should handle interface merging 1`] = ` "declare interface User { firstName: string; @@ -70,11 +75,7 @@ exports[`should handle mutli-extends pattern 1`] = ` declare interface PenStroke { penWidth: number; } -declare type Square = { - sideLength: number, - ... -} & Shape & - PenStroke; +declare type Square = { sideLength: number, ... } & Shape & PenStroke; " `; diff --git a/src/__tests__/__snapshots__/mapped-types.spec.ts.snap b/src/__tests__/__snapshots__/mapped-types.spec.ts.snap index c969ac7c..2b3803bd 100644 --- a/src/__tests__/__snapshots__/mapped-types.spec.ts.snap +++ b/src/__tests__/__snapshots__/mapped-types.spec.ts.snap @@ -1,10 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should handle mapped types 1`] = ` -"declare type Ref = { - current: T | null, - ... -}; +"declare type Ref = { current: T | null, ... }; declare type SourceUnion = \\"a\\" | \\"b\\" | \\"c\\"; declare type SourceObject = { a: number, diff --git a/src/__tests__/__snapshots__/module-identifiers.spec.ts.snap b/src/__tests__/__snapshots__/module-identifiers.spec.ts.snap index 3e23d509..1c8d719a 100644 --- a/src/__tests__/__snapshots__/module-identifiers.spec.ts.snap +++ b/src/__tests__/__snapshots__/module-identifiers.spec.ts.snap @@ -3,10 +3,7 @@ exports[`should handle global types jsx 1`] = ` "import * as React from \\"react\\"; declare function s(node: React$Node): void; -declare type Props = { - children: React$Node, - ... -}; +declare type Props = { children: React$Node, ... }; declare class Component mixins React.Component { render(): React$Node; } diff --git a/src/__tests__/__snapshots__/namespaces.spec.ts.snap b/src/__tests__/__snapshots__/namespaces.spec.ts.snap index 647d9368..3d776c36 100644 --- a/src/__tests__/__snapshots__/namespaces.spec.ts.snap +++ b/src/__tests__/__snapshots__/namespaces.spec.ts.snap @@ -68,10 +68,7 @@ exports[`should handle merging with other types function type 1`] = ` "declare var test: typeof npm$namespace$test; declare var npm$namespace$test: {| (foo: number): string |}; -export type test$Foo = { - bar: number, - ... -}; +export type test$Foo = { bar: number, ... }; " `; @@ -169,10 +166,7 @@ exports[`should handle nested namespace merging function type 1`] = ` declare var npm$namespace$ns: {| test: typeof ns$test |}; declare var npm$namespace$ns$test: {| (foo: number): string |}; -export type ns$test$Foo = { - bar: number, - ... -}; +export type ns$test$Foo = { bar: number, ... }; " `; diff --git a/src/__tests__/__snapshots__/spread.spec.ts.snap b/src/__tests__/__snapshots__/spread.spec.ts.snap index 094d3008..a2791f1c 100644 --- a/src/__tests__/__snapshots__/spread.spec.ts.snap +++ b/src/__tests__/__snapshots__/spread.spec.ts.snap @@ -8,14 +8,8 @@ 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 type Foo = { foo: number, ... }; +declare type Bar = { bar: string, ... }; declare var combination: { ...Foo, ...Bar, @@ -25,12 +19,8 @@ declare var combination: { `; exports[`should use spread when performing union of object types 2`] = ` -"declare type Foo = {| - foo: number, -|}; -declare type Bar = {| - bar: string, -|}; +"declare type Foo = {| foo: number |}; +declare type Bar = {| bar: string |}; declare var combination: {| ...Foo, ...Bar, diff --git a/src/__tests__/__snapshots__/string-literals.spec.ts.snap b/src/__tests__/__snapshots__/string-literals.spec.ts.snap index 221267a2..49bc9ad5 100644 --- a/src/__tests__/__snapshots__/string-literals.spec.ts.snap +++ b/src/__tests__/__snapshots__/string-literals.spec.ts.snap @@ -12,33 +12,15 @@ exports[`should handle string literals in function argument "overloading" 1`] = on(event: \\"close\\", cb: (code: number, message: string) => void): void; on( event: \\"message\\", - cb: ( - data: any, - flags: { - binary: boolean, - ... - } - ) => void + cb: (data: any, flags: { binary: boolean, ... }) => void ): void; on( event: \\"ping\\", - cb: ( - data: any, - flags: { - binary: boolean, - ... - } - ) => void + cb: (data: any, flags: { binary: boolean, ... }) => void ): void; on( event: \\"pong\\", - cb: ( - data: any, - flags: { - binary: boolean, - ... - } - ) => void + cb: (data: any, flags: { binary: boolean, ... }) => void ): void; on(event: \\"open\\", cb: () => void): void; on(event: string, listener: (...args: any[]) => void): void; diff --git a/src/__tests__/__snapshots__/utility-types.spec.ts.snap b/src/__tests__/__snapshots__/utility-types.spec.ts.snap index 8a35786f..9d48f505 100644 --- a/src/__tests__/__snapshots__/utility-types.spec.ts.snap +++ b/src/__tests__/__snapshots__/utility-types.spec.ts.snap @@ -31,17 +31,8 @@ declare type C = $Diff; `; exports[`should handle utility types 1`] = ` -"declare type A = $ReadOnly<{ - a: number, - ... -}>; -declare type B = $Rest< - { - a: number, - ... - }, - { ... } ->; +"declare type A = $ReadOnly<{ a: number, ... }>; +declare type B = $Rest<{ a: number, ... }, { ... }>; declare type C = $NonMaybeType; declare type D = $ReadOnlyArray; declare type E = $Call<((...args: any[]) => R) => R, () => string>; @@ -64,15 +55,8 @@ declare type F2 = { [key: T]: U, ... }; `; exports[`should handle utility types 2`] = ` -"declare type A = $ReadOnly<{| - a: number, -|}>; -declare type B = $Rest< - {| - a: number, - |}, - { ... } ->; +"declare type A = $ReadOnly<{| a: number |}>; +declare type B = $Rest<{| a: number |}, { ... }>; declare type C = $NonMaybeType; declare type D = $ReadOnlyArray; declare type E = $Call<((...args: any[]) => R) => R, () => string>; diff --git a/src/__tests__/__snapshots__/value-exports.spec.ts.snap b/src/__tests__/__snapshots__/value-exports.spec.ts.snap index ed01a222..4ff04f36 100644 --- a/src/__tests__/__snapshots__/value-exports.spec.ts.snap +++ b/src/__tests__/__snapshots__/value-exports.spec.ts.snap @@ -1,19 +1,13 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should handle default exported es module values 1`] = ` -"declare var test: { - a: number, - ... -}; +"declare var test: { a: number, ... }; declare export default typeof test; " `; exports[`should handle exported es module values 1`] = ` -"declare var test: { - a: number, - ... -}; +"declare var test: { a: number, ... }; declare export { test }; " `; diff --git a/src/__tests__/__snapshots__/variables.spec.ts.snap b/src/__tests__/__snapshots__/variables.spec.ts.snap index c34296c7..b07784bd 100644 --- a/src/__tests__/__snapshots__/variables.spec.ts.snap +++ b/src/__tests__/__snapshots__/variables.spec.ts.snap @@ -1,10 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should handle declares 1`] = ` -"declare var test: { - a: number, - ... -}; +"declare var test: { a: number, ... }; declare var foo: string; declare var bar: number; declare var baz: number; diff --git a/src/__tests__/interfaces.spec.ts b/src/__tests__/interfaces.spec.ts index b0aca009..ed94326e 100644 --- a/src/__tests__/interfaces.spec.ts +++ b/src/__tests__/interfaces.spec.ts @@ -41,6 +41,12 @@ interface SpecialUser extends User { }); expect(beautify(result3)).toMatchSnapshot(); expect(result3).toBeValidFlowTypeDeclarations(); + + const result4 = compiler.compileDefinitionString(ts, { + inexact: false, + }); + expect(beautify(result4)).toMatchSnapshot(); + expect(result4).toBeValidFlowTypeDeclarations(); }); it("should handle interface merging", () => { diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 318202c5..82d28b38 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -76,31 +76,6 @@ export const typeMembers = ( .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; -/** - * Print as a Flow object type. - * - * If `forceInexact`, then print as an inexact object type. Otherwise, the - * exactness will be governed by the `inexact` option. - */ -export const objectType = ( - node: ts.InterfaceDeclaration | ts.TypeLiteralNode, - forceInexact: boolean, -): string => { - const isInexact = opts().inexact; - - const members = typeMembers(node).map(m => `\n${m}`); - - if (isInexact) { - members.push("...\n"); - } else if (members.length > 0) { - members.push("\n"); - } - - const inner = members.join(","); - - return isInexact || forceInexact ? `{${inner}}` : `{|${inner}|}`; -}; - /** Print as a Flow interface type's body (the `{…}` portion.) */ const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { const members = typeMembers(node).map(m => `\n${m}`); @@ -251,7 +226,10 @@ export const interfaceDeclaration = ( return `${modifier}type ${nodeName}${printers.common.generics( node.typeParameters, - )} = ${objectType(node, true /* inexact so `&` works */)} ${heritage}`; + )} = ${ + // inexact so `&` works + printers.common.printInexactObjectType(typeMembers(node)) + } ${heritage}`; } else { return `${modifier}interface ${nodeName}${printers.common.generics( node.typeParameters, From de337abcbf7a0edfc599c932ca5ea3bec2312420 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 14:17:54 -0700 Subject: [PATCH 19/28] printers: Fix interfaceRecordType to make inexact types explicit --- .../__snapshots__/interfaces.spec.ts.snap | 16 +++++----------- src/printers/declarations.ts | 19 ++++--------------- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/__tests__/__snapshots__/interfaces.spec.ts.snap b/src/__tests__/__snapshots__/interfaces.spec.ts.snap index d4a10015..2e34be57 100644 --- a/src/__tests__/__snapshots__/interfaces.spec.ts.snap +++ b/src/__tests__/__snapshots__/interfaces.spec.ts.snap @@ -19,24 +19,19 @@ declare type SpecialUser = { nice: number, ... } & User; `; exports[`should handle interface inheritance 2`] = ` -"declare type User = { - firstName: string, -}; +"declare type User = { firstName: string, ... }; declare type SpecialUser = { ...$Exact, - nice: number, + ... }; " `; exports[`should handle interface inheritance 3`] = ` -"declare type User = {| - firstName: string, -|}; +"declare type User = {| firstName: string |}; declare type SpecialUser = {| ...$Exact, - nice: number, |}; " @@ -64,6 +59,7 @@ exports[`should handle interface merging 2`] = ` firstName: string, lastName: string, username: string, + ... }; " `; @@ -87,9 +83,7 @@ exports[`should handle single interface 1`] = ` `; exports[`should handle single interface 2`] = ` -"declare type User = { - firstName: string, -}; +"declare type User = { firstName: string, ... }; " `; diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 82d28b38..39c9a691 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -117,20 +117,10 @@ const interfaceRecordType = ( node: ts.InterfaceDeclaration, heritage: string, ): string => { - const isInexact = opts().inexact; - let members = typeMembers(node) - .map(m => `\n${m}`) - .join(","); - - if (members.length > 0) { - members += "\n"; - } - - if (isInexact) { - return `{${heritage}${members}}`; - } else { - return `{|${heritage}${members}|}`; - } + const members = typeMembers(node); + return printers.common.printDefaultObjectType( + heritage ? [heritage, ...members] : members, + ); }; const classHeritageClause = withEnv< @@ -195,7 +185,6 @@ const interfaceRecordDeclaration = ( .join(",\n"); }) .join(""); - heritage = heritage.length > 0 ? `${heritage},\n` : ""; } const str = `${modifier}type ${nodeName}${printers.common.generics( From e4a8e9697775aba8786e434ba6dbf08fd1a540f9 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:45:07 -0700 Subject: [PATCH 20/28] printers [nfc]: Dissolve interfaceRecordType into its one caller Now that this function uses the common printDefaultObjectType, it's so short that we can simplify the code by just inlining it. That also lets us further simplify, by treating the "heritage" part as just more "members". If it were possible for `node.heritageClauses` to have two or more elements, this change would fix a bug: we weren't putting a comma between the types from different heritage clauses, so we'd write something like `{ ...$Exact...$Exact, x: number }`, a syntax error. But that's only a latent bug: this code handles only interface declarations, not classes, and interfaces can have only an `extends` clause and no other heritage clause. (Classes can have `extends` and also `implements`.) Given that `node.heritageClauses` has at most one element, this new code produces exactly the same type as the old. --- src/printers/declarations.ts | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 39c9a691..fc11b17a 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -113,16 +113,6 @@ const classBody = ( return `{${inner}}`; }; -const interfaceRecordType = ( - node: ts.InterfaceDeclaration, - heritage: string, -): string => { - const members = typeMembers(node); - return printers.common.printDefaultObjectType( - heritage ? [heritage, ...members] : members, - ); -}; - const classHeritageClause = withEnv< { classHeritage?: boolean }, [ts.ExpressionWithTypeArguments], @@ -173,23 +163,22 @@ const interfaceRecordDeclaration = ( node: ts.InterfaceDeclaration, modifier: string, ): string => { - let heritage = ""; + let members: string[] = []; // If the class is extending something if (node.heritageClauses) { - heritage = node.heritageClauses - .map(clause => { - return clause.types - .map(interfaceHeritageClause) - .map(type => `...$Exact<${type}>`) - .join(",\n"); - }) - .join(""); + for (const clause of node.heritageClauses) { + for (const type of clause.types) { + members.push(`...$Exact<${interfaceHeritageClause(type)}>`); + } + } } + members = members.concat(typeMembers(node)); + const str = `${modifier}type ${nodeName}${printers.common.generics( node.typeParameters, - )} = ${interfaceRecordType(node, heritage)}\n`; + )} = ${printers.common.printDefaultObjectType(members)}\n`; return str; }; From 326094e9e6f9df350c5a9924105b7feacbd8579c Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Apr 2022 11:57:04 -0700 Subject: [PATCH 21/28] printers: Fix formatting of static class members This would produce output like: class C { x: number;static y: string ;static z: boolean ; } Instead, produce: class C { x: number; static y: string; static z: boolean; } No snapshots change, because we use Prettier there to normalize such things. --- src/printers/declarations.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index fc11b17a..b11671cf 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -100,7 +100,7 @@ const classBody = ( mergedNamespaceChildren, nodeName, )) { - members.push(`static ${child}\n`); + members.push(`\nstatic ${child}`); } } From a2716e8043412d6101c1226ffd7ee0bc45f49617 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Apr 2022 11:54:38 -0700 Subject: [PATCH 22/28] printers [nfc]: Simplify newlines logic of interfaceTypeBody, classBody This should produce the exact same output. --- src/printers/declarations.ts | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index b11671cf..32d3f9cf 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -78,14 +78,8 @@ export const typeMembers = ( /** Print as a Flow interface type's body (the `{…}` portion.) */ const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { - const members = typeMembers(node).map(m => `\n${m}`); - if (members.length > 0) { - members.push("\n"); - } - - const inner = members.join(","); - - return `{${inner}}`; + const members = typeMembers(node); + return members.length === 0 ? `{}` : `{\n${members.join(",\n")},\n}`; }; const classBody = ( @@ -93,24 +87,18 @@ const classBody = ( nodeName: string, mergedNamespaceChildren: ReadonlyArray>, ): string => { - const members = typeMembers(node).map(m => `\n${m}`); + const members = typeMembers(node); if (mergedNamespaceChildren.length > 0) { for (const child of Namespace.formatChildren( mergedNamespaceChildren, nodeName, )) { - members.push(`\nstatic ${child}`); + members.push(`static ${child}`); } } - if (members.length > 0) { - members.push("\n"); - } - - const inner = members.join(";"); - - return `{${inner}}`; + return members.length === 0 ? `{}` : `{\n${members.join(";\n")},\n}`; }; const classHeritageClause = withEnv< From 92525ea5cc9a1536f4c15cab8e7ce3217bd5156d Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Apr 2022 12:27:59 -0700 Subject: [PATCH 23/28] printers [nfc]: Inline classBody into classDeclaration These were separated by a couple hundred lines of code. Easier to see everything that's going on when they're together. Leave behind a small helper `formatMembers`, which we'll use for interfaces too. --- src/printers/declarations.ts | 39 +++++++++++++++--------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 32d3f9cf..94b7fb93 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -76,31 +76,15 @@ export const typeMembers = ( .filter(Boolean); // Filter rows which didn't print properly (private fields et al) }; +const formatMembers = (members: string[], sep: string) => + members.length ? `{\n${members.join(`${sep}\n`)}${sep}\n}` : `{}`; + /** Print as a Flow interface type's body (the `{…}` portion.) */ const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { const members = typeMembers(node); return members.length === 0 ? `{}` : `{\n${members.join(",\n")},\n}`; }; -const classBody = ( - node: ts.ClassDeclaration, - nodeName: string, - mergedNamespaceChildren: ReadonlyArray>, -): string => { - const members = typeMembers(node); - - if (mergedNamespaceChildren.length > 0) { - for (const child of Namespace.formatChildren( - mergedNamespaceChildren, - nodeName, - )) { - members.push(`static ${child}`); - } - } - - return members.length === 0 ? `{}` : `{\n${members.join(";\n")},\n}`; -}; - const classHeritageClause = withEnv< { classHeritage?: boolean }, [ts.ExpressionWithTypeArguments], @@ -279,11 +263,20 @@ export const classDeclaration = ( heritage = heritage.length > 0 ? `mixins ${heritage}` : ""; } - const str = `declare ${printers.relationships.exporter( + const members = typeMembers(node); + + if (mergedNamespaceChildren.length > 0) { + for (const child of Namespace.formatChildren( + mergedNamespaceChildren, + nodeName, + )) { + members.push(`static ${child}`); + } + } + + return `declare ${printers.relationships.exporter( node, )}class ${nodeName}${printers.common.generics( node.typeParameters, - )} ${heritage} ${classBody(node, nodeName, mergedNamespaceChildren)}`; - - return str; + )} ${heritage} ${formatMembers(members, ";")}`; }; From f371e6db140d331a64d56ec0b3233fe5a46ef034 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Apr 2022 12:34:20 -0700 Subject: [PATCH 24/28] printers [nfc]: Fix a comment in classDeclaration; simplify code slightly This could be an `implements` clause as well as an `extends`, or both. --- src/printers/declarations.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index 94b7fb93..a42eedae 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -252,15 +252,12 @@ export const classDeclaration = ( mergedNamespaceChildren: ReadonlyArray>, ): string => { let heritage = ""; - - // If the class is extending something if (node.heritageClauses) { + // The class is extending and/or implementing something. heritage = node.heritageClauses - .map(clause => { - return clause.types.map(classHeritageClause).join(", "); - }) + .map(clause => clause.types.map(classHeritageClause).join(", ")) .join(", "); - heritage = heritage.length > 0 ? `mixins ${heritage}` : ""; + heritage = `mixins ${heritage}`; } const members = typeMembers(node); From 36674aa17ea87581f741facca13a9457d7c4f14c Mon Sep 17 00:00:00 2001 From: Greg Price Date: Wed, 20 Apr 2022 12:25:14 -0700 Subject: [PATCH 25/28] printers [nfc]: Inline interfaceTypeBody into interfaceDeclaration This function is separated from its one call site by about a hundred lines; bringing them together will make the logic simpler to follow. The function is very short now, so just inline it. --- src/printers/declarations.ts | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/printers/declarations.ts b/src/printers/declarations.ts index a42eedae..7c180f75 100644 --- a/src/printers/declarations.ts +++ b/src/printers/declarations.ts @@ -79,12 +79,6 @@ export const typeMembers = ( const formatMembers = (members: string[], sep: string) => members.length ? `{\n${members.join(`${sep}\n`)}${sep}\n}` : `{}`; -/** Print as a Flow interface type's body (the `{…}` portion.) */ -const interfaceTypeBody = (node: ts.InterfaceDeclaration): string => { - const members = typeMembers(node); - return members.length === 0 ? `{}` : `{\n${members.join(",\n")},\n}`; -}; - const classHeritageClause = withEnv< { classHeritage?: boolean }, [ts.ExpressionWithTypeArguments], @@ -166,7 +160,9 @@ export const interfaceDeclaration = ( } if (node.heritageClauses) { - // The interface is extending something. + // The interface is extending something. Represent it as an + // intersection with an inexact object type. + let heritage = node.heritageClauses .map(clause => { return clause.types.map(interfaceHeritageClause).join(" & "); @@ -181,9 +177,12 @@ export const interfaceDeclaration = ( printers.common.printInexactObjectType(typeMembers(node)) } ${heritage}`; } else { + // The interface isn't extending anything. Represent it as a + // Flow interface. + return `${modifier}interface ${nodeName}${printers.common.generics( node.typeParameters, - )} ${interfaceTypeBody(node)} `; + )} ${formatMembers(typeMembers(node), ",")} `; } }; From df16b228b824c31ce8d7e81b3e05b0fc3dece8e5 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Mon, 18 Apr 2022 15:09:49 -0700 Subject: [PATCH 26/28] printers: Drop now-redundant comments on enum members Originally the implementation of enum members was rather indirect, and these comments provided useful information about the original value. But since a2cd5f01d which simplified the implementation, the value shown earlier on the same line has had exactly the same content as the comment. So we can now drop the comment without losing anything. --- .../__snapshots__/enums.spec.ts.snap | 30 +++++++++---------- .../__snapshots__/namespaces.spec.ts.snap | 14 ++++----- src/printers/declarations.ts | 3 +- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/__tests__/__snapshots__/enums.spec.ts.snap b/src/__tests__/__snapshots__/enums.spec.ts.snap index 60d43826..308a6525 100644 --- a/src/__tests__/__snapshots__/enums.spec.ts.snap +++ b/src/__tests__/__snapshots__/enums.spec.ts.snap @@ -2,9 +2,9 @@ exports[`should handle basic enums: class 1`] = ` "declare var Label: {| - +LABEL_OPTIONAL: 0, // 0 - +LABEL_REQUIRED: 1, // 1 - +LABEL_REPEATED: 2, // 2 + +LABEL_OPTIONAL: 0, + +LABEL_REQUIRED: 1, + +LABEL_REPEATED: 2, |}; declare type A = $Values; declare type B = typeof Label.LABEL_OPTIONAL; @@ -18,8 +18,8 @@ exports[`should handle empty enums: class 1`] = ` exports[`should handle importing enum types: class 1`] = ` "declare export var Label: {| - +A: \\"A\\", // \\"A\\" - +B: \\"B\\", // \\"B\\" + +A: \\"A\\", + +B: \\"B\\", |}; " `; @@ -32,8 +32,8 @@ declare export function foo(label: $Values