Repository: prettier/prettier (JavaScript)

Issue:
Object destructuring in parameters with type and comments results in non-stable format loop
**Prettier 3.1.0**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAdKcAeAHCAnGAAgBM4AzAQwFcAbIsGigZycIDEIJDh1DDIoTGHipgY+ABTBCvPoTKcANLL4B6VYQBGFPIQC8MkNryoQyqHMLqtFAF639h7fdPm+AXyTtOASSgBLGABKbnd0dzMQCGwYf2gmZFAdPAgAdwAFHQQElAoaVIoATwTFIzwKMABrOBgAZQoAWzgAGX8MZEoaJjhSzXKqmtrsCraAc2RhKh6Qbob-CZFprGw4PH8m2DyAFVWoHX84HM7u0qYxmjgARSoIeA68k5AAKyZMWvOrm7ukY+mARy+cHSKWwORAzAAtBg4KRiJFhBR-DQxgBhCANBoUZDgmg0SJnKCjC4AQRgwn8mio8HSq1a7R+D2mAAsYA0aAB1JmBQ7DMBwWrZQL+ABugUK2LALEiwqmflIsFqYDWMWJUGItRghQu9y602wKW67PK2Gx+sOq2FcEibW6BGBFFGmJ1j2GeFt2M1KyYSv8MUi+raMHZ-mIMCZyAAHAAGUp4OAA-xx+2OrEM3WlGAUTTB0PhpAAJlKVG6WyzR0ZpTgDU0MNhzQohKoDrgHDwmLJY2x1HEIHc7iAA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```ts
export default class Foo {
  constructor({ 
    foo,
    // bar = "bar",
    // bazz = "bazz",
  }: FooInit) {}
}
```

**Output:**
<!-- prettier-ignore -->
```ts
export default class Foo {
  constructor({
    foo,
  } // bar = "bar",
  // bazz = "bazz",
  : FooInit) {}
}
```

**Expected behavior:**

I would expect no changes whatsoever to the original input:

```ts
export default class Foo {
  constructor({ 
    foo,
    // bar = "bar",
    // bazz = "bazz",
  }: FooInit) {}
}
```

**Notes**

This behaviour is actually fairly buggy, not only is the output not desirable, the output *isn't stable*, feeding the output back into prettier changes it again:

```ts
export default class Foo {
  constructor({
    foo, // bar = "bar",
  } // bazz = "bazz",
  : FooInit) {}
}
```

which feeding back in becomes (and will from now on alternate between this and the above printing):

```ts
export default class Foo {
  constructor({
    foo, // bar = "bar",
    // bazz = "bazz",
  }: FooInit) {}
}
```



Hints:

Patch:
diff --git a/src/language-js/comments/handle-comments.js b/src/language-js/comments/handle-comments.js
--- a/src/language-js/comments/handle-comments.js
+++ b/src/language-js/comments/handle-comments.js
@@ -62,6 +62,7 @@ function handleOwnLineComment(context) {
     handleLabeledStatementComments,
     handleBreakAndContinueStatementComments,
     handleNestedConditionalExpressionComments,
+    handleCommentsInDestructuringPattern,
   ].some((fn) => fn(context));
 }
 
@@ -976,6 +977,44 @@ function handleLastUnionElementInExpression({
   return false;
 }
 
+/**
+ * const [
+ *   foo,
+ *   // bar
+ *   // baz
+ * ]: Foo = foo();
+ *
+ * const {
+ *   foo,
+ *   // bar
+ *   // baz
+ * }: Foo = foo();
+ *
+ */
+function handleCommentsInDestructuringPattern({
+  comment,
+  enclosingNode,
+  precedingNode,
+  followingNode,
+}) {
+  if (
+    (enclosingNode?.type === "ObjectPattern" ||
+      enclosingNode?.type === "ArrayPattern") &&
+    followingNode?.type === "TSTypeAnnotation"
+  ) {
+    if (precedingNode) {
+      addTrailingComment(precedingNode, comment);
+    } else {
+      // const {
+      //   // bar
+      //   // baz
+      // }: Foo = expr;
+      addDanglingComment(enclosingNode, comment);
+    }
+    return true;
+  }
+}
+
 /**
  * @param {Node} node
  * @returns {boolean}


Test patch:
diff --git a/tests/format/typescript/comments/15707.ts b/tests/format/typescript/comments/15707.ts
new file mode 100644
--- /dev/null
+++ b/tests/format/typescript/comments/15707.ts
@@ -0,0 +1,55 @@
+const {
+  foo,
+  // bar
+  // baz
+}: Foo = expr;
+
+const {
+  foo1,
+  // bar
+  foo2,
+  // baz
+}: Foo = expr;
+
+const [
+  foo,
+  // bar
+  // baz
+]: Foo = expr;
+
+const [
+  foo1,
+  // bar
+  foo2,
+  // baz
+]: Foo = expr;
+
+function method({
+  foo,
+  // bar = "bar",
+  // bazz = "bazz",
+}: Foo) {}
+
+function method({
+  foo1,
+  // bar = "bar",
+  foo2
+  // bazz = "bazz",
+}: Foo) {}
+
+function method([
+  foo,
+  // bar = "bar",
+  foo2
+  // bazz = "bazz",
+]: Foo) {}
+
+const {
+  // bar
+  // baz
+}: Foo = expr;
+
+const [
+  // bar
+  // baz
+]: Foo = expr;
diff --git a/tests/format/typescript/comments/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/comments/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/typescript/comments/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/comments/__snapshots__/jsfmt.spec.js.snap
@@ -1,5 +1,127 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`15707.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+const {
+  foo,
+  // bar
+  // baz
+}: Foo = expr;
+
+const {
+  foo1,
+  // bar
+  foo2,
+  // baz
+}: Foo = expr;
+
+const [
+  foo,
+  // bar
+  // baz
+]: Foo = expr;
+
+const [
+  foo1,
+  // bar
+  foo2,
+  // baz
+]: Foo = expr;
+
+function method({
+  foo,
+  // bar = "bar",
+  // bazz = "bazz",
+}: Foo) {}
+
+function method({
+  foo1,
+  // bar = "bar",
+  foo2
+  // bazz = "bazz",
+}: Foo) {}
+
+function method([
+  foo,
+  // bar = "bar",
+  foo2
+  // bazz = "bazz",
+]: Foo) {}
+
+const {
+  // bar
+  // baz
+}: Foo = expr;
+
+const [
+  // bar
+  // baz
+]: Foo = expr;
+
+=====================================output=====================================
+const {
+  foo,
+  // bar
+  // baz
+}: Foo = expr;
+
+const {
+  foo1,
+  // bar
+  foo2,
+  // baz
+}: Foo = expr;
+
+const [
+  foo,
+  // bar
+  // baz
+]: Foo = expr;
+
+const [
+  foo1,
+  // bar
+  foo2,
+  // baz
+]: Foo = expr;
+
+function method({
+  foo,
+  // bar = "bar",
+  // bazz = "bazz",
+}: Foo) {}
+
+function method({
+  foo1,
+  // bar = "bar",
+  foo2,
+  // bazz = "bazz",
+}: Foo) {}
+
+function method([
+  foo,
+  // bar = "bar",
+  foo2,
+  // bazz = "bazz",
+]: Foo) {}
+
+const {
+  // bar
+  // baz
+}: Foo = expr;
+
+const [
+  // bar
+  // baz
+]: Foo = expr;
+
+================================================================================
+`;
+
 exports[`abstract_class.ts format 1`] = `
 ====================================options=====================================
 parsers: ["typescript"]