diff --git a/changelog_unreleased/flow/19102.md b/changelog_unreleased/flow/19102.md new file mode 100644 index 000000000..510ffb64d --- /dev/null +++ b/changelog_unreleased/flow/19102.md @@ -0,0 +1,19 @@ +#### Add support for `writeonly`, `in`, and `out` variance modifiers (#19102 by @marcoww6) + +Support new Flow variance syntax: `writeonly` on object type properties/indexers, and `in T`/`out T` on type parameters. + + +```jsx +// Input +type T = {writeonly foo: string}; +type Contravariant = T; +type Covariant = T; + +// Prettier stable +// SyntaxError + +// Prettier main +type T = { writeonly foo: string }; +type Contravariant = T; +type Covariant = T; +``` diff --git a/cspell.json b/cspell.json index 55224441e..7d7dc3a8e 100644 --- a/cspell.json +++ b/cspell.json @@ -305,6 +305,7 @@ "webstorm", "Weixin", "withastro", + "writeonly", "wxss", "xdescribe", "xiechao", diff --git a/src/language-js/print/flow.js b/src/language-js/print/flow.js index ad8e71d2d..9447bfd6e 100644 --- a/src/language-js/print/flow.js +++ b/src/language-js/print/flow.js @@ -218,7 +218,14 @@ function printFlow(path, options, print, args) { return ["?", print("typeAnnotation")]; case "Variance": { const { kind } = node; - assert.ok(kind === "plus" || kind === "minus" || kind === "readonly"); + assert.ok( + kind === "plus" || + kind === "minus" || + kind === "readonly" || + kind === "writeonly" || + kind === "in" || + kind === "out", + ); return kind === "plus" ? "+" : kind === "minus" ? "-" : `${kind} `; } case "KeyofTypeAnnotation": diff --git a/tests/format/flow/in-out-variance/__snapshots__/format.test.js.snap b/tests/format/flow/in-out-variance/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..9b723f176 --- /dev/null +++ b/tests/format/flow/in-out-variance/__snapshots__/format.test.js.snap @@ -0,0 +1,38 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`in-out-variance.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] + printWidth: 80 (default) | +=====================================input====================================== +type Contravariant< in T > = T; + +type Covariant< out T > = T; + +type Mixed< in T, out U > = T; + +type InNamedIn< in in > = void; + +type OutNamedOut< out out > = void; + +type InWithBound< in T: number > = T; + +type OutWithDefault< out T = string > = T; + +=====================================output===================================== +type Contravariant = T; + +type Covariant = T; + +type Mixed = T; + +type InNamedIn = void; + +type OutNamedOut = void; + +type InWithBound = T; + +type OutWithDefault = T; + +================================================================================ +`; diff --git a/tests/format/flow/in-out-variance/format.test.js b/tests/format/flow/in-out-variance/format.test.js new file mode 100644 index 000000000..8fbbcfa61 --- /dev/null +++ b/tests/format/flow/in-out-variance/format.test.js @@ -0,0 +1,5 @@ +runFormatTest(import.meta, ["flow"], { + errors: { + "babel-flow": true, + }, +}); diff --git a/tests/format/flow/in-out-variance/in-out-variance.js b/tests/format/flow/in-out-variance/in-out-variance.js new file mode 100644 index 000000000..c20be233e --- /dev/null +++ b/tests/format/flow/in-out-variance/in-out-variance.js @@ -0,0 +1,13 @@ +type Contravariant< in T > = T; + +type Covariant< out T > = T; + +type Mixed< in T, out U > = T; + +type InNamedIn< in in > = void; + +type OutNamedOut< out out > = void; + +type InWithBound< in T: number > = T; + +type OutWithDefault< out T = string > = T; diff --git a/tests/format/flow/variance/__snapshots__/format.test.js.snap b/tests/format/flow/variance/__snapshots__/format.test.js.snap index 6638d85f9..b92123817 100644 --- a/tests/format/flow/variance/__snapshots__/format.test.js.snap +++ b/tests/format/flow/variance/__snapshots__/format.test.js.snap @@ -24,6 +24,13 @@ type ReadonlyInterface = interface { readonly prop: string }; +type ReadonlyReservedWords = { + readonly with: string, + readonly default?: string, + readonly enum: number, + readonly new: boolean, +}; + =====================================output===================================== class Route { static +param: T; @@ -44,5 +51,12 @@ type ReadonlyInterface = interface { readonly prop: string, }; +type ReadonlyReservedWords = { + readonly with: string, + readonly default?: string, + readonly enum: number, + readonly new: boolean, +}; + ================================================================================ `; diff --git a/tests/format/flow/variance/variance.js b/tests/format/flow/variance/variance.js index 1858cd7d2..94c26eda0 100644 --- a/tests/format/flow/variance/variance.js +++ b/tests/format/flow/variance/variance.js @@ -16,3 +16,10 @@ type ReadonlyTuple = [ readonly label: number ]; type ReadonlyInterface = interface { readonly prop: string }; + +type ReadonlyReservedWords = { + readonly with: string, + readonly default?: string, + readonly enum: number, + readonly new: boolean, +}; diff --git a/tests/format/flow/writeonly/__snapshots__/format.test.js.snap b/tests/format/flow/writeonly/__snapshots__/format.test.js.snap new file mode 100644 index 000000000..ebe67cd61 --- /dev/null +++ b/tests/format/flow/writeonly/__snapshots__/format.test.js.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing + +exports[`writeonly.js format 1`] = ` +====================================options===================================== +parsers: ["flow"] + printWidth: 80 (default) | +=====================================input====================================== +type WriteonlyObj = { + writeonly foo: string, + writeonly [string]: unknown +}; + +type WriteonlyReservedWords = { + writeonly with: string, + writeonly default?: string, +}; + +=====================================output===================================== +type WriteonlyObj = { + writeonly foo: string, + writeonly [string]: unknown, +}; + +type WriteonlyReservedWords = { + writeonly with: string, + writeonly default?: string, +}; + +================================================================================ +`; diff --git a/tests/format/flow/writeonly/format.test.js b/tests/format/flow/writeonly/format.test.js new file mode 100644 index 000000000..8fbbcfa61 --- /dev/null +++ b/tests/format/flow/writeonly/format.test.js @@ -0,0 +1,5 @@ +runFormatTest(import.meta, ["flow"], { + errors: { + "babel-flow": true, + }, +}); diff --git a/tests/format/flow/writeonly/writeonly.js b/tests/format/flow/writeonly/writeonly.js new file mode 100644 index 000000000..d8226ce67 --- /dev/null +++ b/tests/format/flow/writeonly/writeonly.js @@ -0,0 +1,9 @@ +type WriteonlyObj = { + writeonly foo: string, + writeonly [string]: unknown +}; + +type WriteonlyReservedWords = { + writeonly with: string, + writeonly default?: string, +};