Repository: prettier/prettier (JavaScript)

Issue:
Comment in empty array type breaks code
**Prettier 2.7.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEBLWcBOAzAhmOAAgA1DgAdKQwgDyUIG1LrqB6V25wgXQG5KAviAA0ICAAcYqaAGdkoXJkwQA7gAVFCOSlwAbFbgCec0QCNM+ANZwYAZVwBbOABl0cZHl0y4Zi2Gt24vjoAObIMJgArj4gcA6mcAAmiUnOuFAhkbghcABiEJgOuDBSGcgguJEwECIgABYwDroA6nWo8DJBBLZa7agAbu2G5WAyJmhQ3pgwahYhRR563qIAVjI0tqG6cACKkRDwi14xQZhT5TCG4nAyYJiokrXi97DNqIkwdcgAHAAMos8IN5mhZxOVnjcsP13KIAI77eCzCTaCoyAC0UDgSSStUwcHhqDxs2yCyQnmWIG8DlQ4SiMRkW12CPcZKWMRguFMbw+XyQACZRBFcKhdKEAMIQByk2IyACstUi3gAKpztOSYv1ogBJKApWC2O4PGAAQV1tku2yO3gEAiAA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
interface X {
  x: [
    // x
  ];
}
```

**Output:**
<!-- prettier-ignore -->
```tsx
interface X {
  x: [// x];
}

```
The code is broken and no longer parsable as correct typescript. This happens both with "typescript" and "babel-ts" parsers.

**Expected behavior:**
Output should stay the same as input.

This is similar to https://github.com/prettier/prettier/issues/9299 but there it breaks only if there's a colon before the comment but with typescript it breaks any empty array type.


Hints:
Related comment about refactoring of dangling comments: https://github.com/prettier/prettier/pull/13445#issuecomment-1240723341

Patch:
diff --git a/src/language-js/print/array.js b/src/language-js/print/array.js
--- a/src/language-js/print/array.js
+++ b/src/language-js/print/array.js
@@ -23,6 +23,19 @@ import { printOptionalToken, printTypeAnnotation } from "./misc.js";
 
 /** @typedef {import("../../document/builders.js").Doc} Doc */
 
+function printEmptyArray(path, openBracket, closeBracket, options) {
+  const { node } = path;
+  if (!hasComment(node, CommentCheckFlags.Dangling)) {
+    return [openBracket, closeBracket];
+  }
+  return group([
+    openBracket,
+    printDanglingComments(path, options),
+    softline,
+    closeBracket,
+  ]);
+}
+
 function printArray(path, options, print) {
   const { node } = path;
   /** @type{Doc[]} */
@@ -31,18 +44,7 @@ function printArray(path, options, print) {
   const openBracket = node.type === "TupleExpression" ? "#[" : "[";
   const closeBracket = "]";
   if (node.elements.length === 0) {
-    if (!hasComment(node, CommentCheckFlags.Dangling)) {
-      parts.push(openBracket, closeBracket);
-    } else {
-      parts.push(
-        group([
-          openBracket,
-          printDanglingComments(path, options),
-          softline,
-          closeBracket,
-        ])
-      );
-    }
+    parts.push(printEmptyArray(path, openBracket, closeBracket, options));
   } else {
     const lastElem = node.elements.at(-1);
     const canHaveTrailingComma = !(lastElem && lastElem.type === "RestElement");
@@ -187,4 +189,9 @@ function printArrayItemsConcisely(path, options, print, trailingComma) {
   return fill(parts);
 }
 
-export { printArray, printArrayItems, isConciselyPrintedArray };
+export {
+  printArray,
+  printArrayItems,
+  isConciselyPrintedArray,
+  printEmptyArray,
+};
diff --git a/src/language-js/print/type-annotation.js b/src/language-js/print/type-annotation.js
--- a/src/language-js/print/type-annotation.js
+++ b/src/language-js/print/type-annotation.js
@@ -1,5 +1,4 @@
-import { printComments, printDanglingComments } from "../../main/comments.js";
-import { isNonEmptyArray } from "../../common/util.js";
+import { printComments } from "../../main/comments.js";
 import {
   group,
   join,
@@ -23,7 +22,7 @@ import {
   printFunctionParameters,
   shouldGroupFunctionParameters,
 } from "./function-parameters.js";
-import { printArrayItems } from "./array.js";
+import { printArrayItems, printEmptyArray } from "./array.js";
 
 function shouldHugType(node) {
   if (isSimpleType(node) || isObjectType(node)) {
@@ -289,18 +288,18 @@ function printTupleType(path, options, print) {
   const { node } = path;
   const typesField = node.type === "TSTupleType" ? "elementTypes" : "types";
   const types = node[typesField];
-  const isNonEmptyTuple = isNonEmptyArray(types);
-  const bracketsDelimiterLine = isNonEmptyTuple ? softline : "";
+  const isEmptyTuple = types.length === 0;
+  const openBracket = "[";
+  const closeBracket = "]";
+  if (isEmptyTuple) {
+    return printEmptyArray(path, openBracket, closeBracket, options);
+  }
   return group([
-    "[",
-    indent([
-      bracketsDelimiterLine,
-      printArrayItems(path, options, typesField, print),
-    ]),
-    ifBreak(isNonEmptyTuple && shouldPrintComma(options, "all") ? "," : ""),
-    printDanglingComments(path, options, /* sameIndent */ true),
-    bracketsDelimiterLine,
-    "]",
+    openBracket,
+    indent([softline, printArrayItems(path, options, typesField, print)]),
+    ifBreak(shouldPrintComma(options, "all") ? "," : ""),
+    softline,
+    closeBracket,
   ]);
 }
 


Test patch:
diff --git a/tests/format/typescript/tuple/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/tuple/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/typescript/tuple/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/tuple/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,203 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`dangling-comments.ts - {"trailingComma":"all"} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+trailingComma: "all"
+                                                                                | printWidth
+=====================================input======================================
+type Foo1 = [/* comment */];
+
+type Foo2 = [
+    // comment
+];
+
+type Foo3 = [
+    // comment1
+    // comment2
+];
+
+type Foo4 = [
+    // comment1
+
+    // comment2
+];
+
+type Foo5 = [
+    /* comment1 */
+];
+
+type Foo6 = [
+    /* comment1 */
+
+    /* comment2 */
+];
+
+
+=====================================output=====================================
+type Foo1 = [
+  /* comment */
+];
+
+type Foo2 = [
+  // comment
+];
+
+type Foo3 = [
+  // comment1
+  // comment2
+];
+
+type Foo4 = [
+  // comment1
+  // comment2
+];
+
+type Foo5 = [
+  /* comment1 */
+];
+
+type Foo6 = [
+  /* comment1 */
+  /* comment2 */
+];
+
+================================================================================
+`;
+
+exports[`dangling-comments.ts - {"trailingComma":"es5"} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+trailingComma: "es5"
+                                                                                | printWidth
+=====================================input======================================
+type Foo1 = [/* comment */];
+
+type Foo2 = [
+    // comment
+];
+
+type Foo3 = [
+    // comment1
+    // comment2
+];
+
+type Foo4 = [
+    // comment1
+
+    // comment2
+];
+
+type Foo5 = [
+    /* comment1 */
+];
+
+type Foo6 = [
+    /* comment1 */
+
+    /* comment2 */
+];
+
+
+=====================================output=====================================
+type Foo1 = [
+  /* comment */
+];
+
+type Foo2 = [
+  // comment
+];
+
+type Foo3 = [
+  // comment1
+  // comment2
+];
+
+type Foo4 = [
+  // comment1
+  // comment2
+];
+
+type Foo5 = [
+  /* comment1 */
+];
+
+type Foo6 = [
+  /* comment1 */
+  /* comment2 */
+];
+
+================================================================================
+`;
+
+exports[`dangling-comments.ts - {"trailingComma":"none"} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+trailingComma: "none"
+                                                                                | printWidth
+=====================================input======================================
+type Foo1 = [/* comment */];
+
+type Foo2 = [
+    // comment
+];
+
+type Foo3 = [
+    // comment1
+    // comment2
+];
+
+type Foo4 = [
+    // comment1
+
+    // comment2
+];
+
+type Foo5 = [
+    /* comment1 */
+];
+
+type Foo6 = [
+    /* comment1 */
+
+    /* comment2 */
+];
+
+
+=====================================output=====================================
+type Foo1 = [
+  /* comment */
+];
+
+type Foo2 = [
+  // comment
+];
+
+type Foo3 = [
+  // comment1
+  // comment2
+];
+
+type Foo4 = [
+  // comment1
+  // comment2
+];
+
+type Foo5 = [
+  /* comment1 */
+];
+
+type Foo6 = [
+  /* comment1 */
+  /* comment2 */
+];
+
+================================================================================
+`;
+
 exports[`trailing-comma.ts - {"trailingComma":"all"} format 1`] = `
 ====================================options=====================================
 parsers: ["typescript"]
diff --git a/tests/format/typescript/tuple/dangling-comments.ts b/tests/format/typescript/tuple/dangling-comments.ts
new file mode 100644
--- /dev/null
+++ b/tests/format/typescript/tuple/dangling-comments.ts
@@ -0,0 +1,27 @@
+type Foo1 = [/* comment */];
+
+type Foo2 = [
+    // comment
+];
+
+type Foo3 = [
+    // comment1
+    // comment2
+];
+
+type Foo4 = [
+    // comment1
+
+    // comment2
+];
+
+type Foo5 = [
+    /* comment1 */
+];
+
+type Foo6 = [
+    /* comment1 */
+
+    /* comment2 */
+];
+