Repository: prettier/prettier (JavaScript)
Date: 2019-08-30 19:10:56+00:00
Task type: Bug Fix

Issue:
Invalid trailing comma insertion for empty destructured argument
**Prettier 1.18.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuc0DOMAEAzTBeTACmAF8lMBZATwDc4AnKgG2gHMYqAHOauxlqOy49aDZmw7deYgUO4BKfAD5MpANwAdKCAA0ICJxgBLdMlABDevQgB3AAqWEaZCHM0IRgCa6QAI3rmYADWcDAAypyBRoLIMPQArnB6ABYwALZMAOrJRvBokWBwYU65RjS5VC5gaM560WgMMHYBrGnmyNjmTA16AFZoAB4AQgHBoWHmaXAAMtFwHV09IP0DYdGsTHAAivEQ8AvdSSCR9A30Lr7mvuLaepz00TCZXjDJyAAcAAx31g2ZAZwXPc4Gc6D4AI67eDNAzOFDmNAAWigcDgnjRPnocEhRixzXMrXaSE6hz0DTSRliCSOaHWmx2e3mxMWRxgV2enleyAATHo4uYjEx1gBhCBpNouEEAVh88QaABUrnCSQ0SCQgA)
```sh
--parser babylon
--trailing-comma es5
```

**Input:**
```jsx
const f = ({}: MyverylongtypeMyverylongtypeMyverylongtypeMyverylongtype) => {};

```

**Output:**
```jsx
const f = ({
  ,
}: MyverylongtypeMyverylongtypeMyverylongtypeMyverylongtype) => {};

```
Note that the above is invalid JavaScript/TypeScript!

**Second Output:**
```jsx
SyntaxError: Unexpected token (2:3)
  1 | const f = ({
> 2 |   ,
    |   ^
  3 | }: MyverylongtypeMyverylongtypeMyverylongtypeMyverylongtype) => {};
  4 | 
```

**Expected behavior:**
Either
```tsx
const f = ({
}: MyverylongtypeMyverylongtypeMyverylongtypeMyverylongtype) => {};

```
or maybe
```tsx
const f = (
  {}: MyverylongtypeMyverylongtypeMyverylongtypeMyverylongtype,
) => {};

```


Issue discussion:
Must be related. Such empty patterns are formatted differently when type annotations are added:

**Prettier 1.18.2**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AEqAFMAL4CU+ZAOlBlrgcWUvgGIQSVkgA0IEAA6MoAZ2SgAhgCdpEAO4AFGQnEpJANwg4AJnxAAjaZLABrODADKgkzigBzZDGno4-ABYwAtgBsA6u448KI2YHCWqkE4GkEAnsjgouL8dqJw0jCKxvZeksiokj5p-ABWogAeAELGZhaWkl5wADJ2cPmFxSBl5ZZ29j5wAIroEPDtRW4gNtJp0gmoPgr6gtJ2MH66MO7IABwADPwrEGl+xoIJK3CzGm38AI4j8FlCaiCSogC0UHBwOr-60jgDxwgKykhyeSQBQm-DSXhwThck1EfQGw1GbShHUmMEkBg2Oi2yAATPxnJIcD4+gBhCBeXIJKDQW4gdBpAAqeLU0LSpFIQA)
```sh
--parser flow
```

**Input:**
```jsx
function f({}) {}
function f({}: Foo) {}
```

**Output:**
```jsx
function f({}) {}
function f({  }: Foo) {}

```

Solution patch:
diff --git a/CHANGELOG.unreleased.md b/CHANGELOG.unreleased.md
index d7e1b48b4d1f..76fde807d0f2 100644
--- a/CHANGELOG.unreleased.md
+++ b/CHANGELOG.unreleased.md
@@ -504,6 +504,25 @@ class Class {
 }
 ```
 
+#### JavaScript: Handle empty object patterns with type annotations in function parameters ([#6438] by [@bakkot])
+
+<!-- prettier-ignore -->
+```js
+// Input
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+
+// Output (Prettier stable)
+const f = ({
+  ,
+}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({  }: Foo) {}
+
+// Output (Prettier master)
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+```
+
 [#5910]: https://github.com/prettier/prettier/pull/5910
 [#6186]: https://github.com/prettier/prettier/pull/6186
 [#6206]: https://github.com/prettier/prettier/pull/6206
@@ -521,6 +540,7 @@ class Class {
 [#6412]: https://github.com/prettier/prettier/pull/6412
 [#6420]: https://github.com/prettier/prettier/pull/6420
 [#6411]: https://github.com/prettier/prettier/pull/6411
+[#6438]: https://github.com/prettier/prettier/pull/6411
 [@duailibe]: https://github.com/duailibe
 [@gavinjoyce]: https://github.com/gavinjoyce
 [@sosukesuzuki]: https://github.com/sosukesuzuki
diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js
index dad577e6a75d..e010dc7e664d 100644
--- a/src/language-js/printer-estree.js
+++ b/src/language-js/printer-estree.js
@@ -1379,9 +1379,13 @@ function printPathNoParens(path, options, print, args) {
       );
 
       let content;
-      if (props.length === 0 && !n.typeAnnotation) {
+      if (props.length === 0) {
         if (!hasDanglingComments(n)) {
-          return concat([leftBrace, rightBrace]);
+          return concat([
+            leftBrace,
+            rightBrace,
+            printTypeAnnotation(path, options, print)
+          ]);
         }
 
         content = group(
@@ -1390,7 +1394,8 @@ function printPathNoParens(path, options, print, args) {
             comments.printDanglingComments(path, options),
             softline,
             rightBrace,
-            printOptionalToken(path)
+            printOptionalToken(path),
+            printTypeAnnotation(path, options, print)
           ])
         );
       } else {


Test patch:
diff --git a/tests/flow_parameter_with_type/__snapshots__/jsfmt.spec.js.snap b/tests/flow_parameter_with_type/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..42006eb9e42c
--- /dev/null
+++ b/tests/flow_parameter_with_type/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,34 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`param.js 1`] = `
+====================================options=====================================
+parsers: ["flow", "babel"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+
+=====================================output=====================================
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+
+================================================================================
+`;
+
+exports[`param.js 2`] = `
+====================================options=====================================
+parsers: ["flow", "babel"]
+printWidth: 80
+trailingComma: "all"
+                                                                                | printWidth
+=====================================input======================================
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+
+=====================================output=====================================
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}
+
+================================================================================
+`;
diff --git a/tests/flow_parameter_with_type/jsfmt.spec.js b/tests/flow_parameter_with_type/jsfmt.spec.js
new file mode 100644
index 000000000000..381dfaee8ca6
--- /dev/null
+++ b/tests/flow_parameter_with_type/jsfmt.spec.js
@@ -0,0 +1,2 @@
+run_spec(__dirname, ["flow", "babel"]);
+run_spec(__dirname, ["flow", "babel"], { trailingComma: "all" });
diff --git a/tests/flow_parameter_with_type/param.js b/tests/flow_parameter_with_type/param.js
new file mode 100644
index 000000000000..45e4f4c62c2f
--- /dev/null
+++ b/tests/flow_parameter_with_type/param.js
@@ -0,0 +1,2 @@
+const f = ({}: MyVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLongType) => {};
+function g({}: Foo) {}