Repository: prettier/prettier (JavaScript)

Issue:
Regression in v3
**Prettier pr-14803**
[Playground link](https://deploy-preview-14803--prettier.netlify.app/playground/#N4Igxg9gdgLgprEAuEAzArlMMCW0AEAtgIYDWcAwgBbE5QDqxANuQE4A8AOlPvgIKsA5gBV8cAB7woAEwDO+YN168IAB1zRZSfADVmOacXjSA8urxRZAbiXLpOVlGKE422TFZ1BNnstQ4mOFUjKgB+Nw8vH14AXwAabgA+AAoASgUYkDiQNQ1LZFBiVlYIAHcABSKEWWQQZlLiAE8a7IAjVmIwchgAZWc4ABk6OGRUZlk4No6uuF7gsC9kD3RJkAnCHCXWFezZL0CARXQIeFHx1YArWXEe-bgjk5GkMaYJ7IBHY-hyktUalGIsgAtFA4HBpOCsiAPLQmF4KBBCCRaswmFC9lBBIE+DBIq10N84KwhqCzq9VlQYIQmPQqDh4LJ5nAetV6TgAG70xq1MCyFogdkrACSMgQvTAnnUfBkPRgjUCZLeIFUJQm9A6qlqKrgE1Y7JG2ToupgP2IgmRz3O2WCrF1tTlqh1Epw6ihKroMHoBhgVGQAA4AAzZVhwT4OOCm83ERWrGDEVpe6Q+5AAJmy6Amwnj-xeSrghFa4Ih0gGxEx6DNcAAYhBWCRcYsAQSICAYjEgA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
function makeChainWalker<
  ArgT extends {
    options: ValidatedOptions;
    dirname: string;
    filepath?: string;
  },
>() {}
```

**Output:**
<!-- prettier-ignore -->
```tsx
function makeChainWalker<
  ArgT extends
    {
      options: ValidatedOptions;
      dirname: string;
      filepath?: string;
    },
>() {}

```

**Expected behavior:**

(V2 output should be preferred)

```tsx
function makeChainWalker<
  ArgT extends {
    options: ValidatedOptions;
    dirname: string;
    filepath?: string;
  }
>() {}
```

Maybe the same problem as https://github.com/prettier/prettier/issues/14809

https://github.com/babel/babel/pull/15617/files#r1187671092



Hints:

Patch:
diff --git a/src/language-js/print/type-parameters.js b/src/language-js/print/type-parameters.js
--- a/src/language-js/print/type-parameters.js
+++ b/src/language-js/print/type-parameters.js
@@ -7,6 +7,8 @@ import {
   group,
   indent,
   ifBreak,
+  lineSuffixBoundary,
+  indentIfBreak,
 } from "../../document/builders.js";
 import {
   isTestCall,
@@ -171,7 +173,13 @@ function printTypeParameter(path, options, print) {
   }
 
   if (node.constraint) {
-    parts.push(" extends", indent([line, print("constraint")]));
+    const groupId = Symbol("constraint");
+    parts.push(
+      " extends",
+      group(indent(line), { id: groupId }),
+      lineSuffixBoundary,
+      indentIfBreak(print("constraint"), { groupId })
+    );
   }
 
   if (node.default) {


Test patch:
diff --git a/tests/format/typescript/typeparams/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/typeparams/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/typescript/typeparams/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/typeparams/__snapshots__/jsfmt.spec.js.snap
@@ -404,12 +404,91 @@ export type OuterType3<
 
 export type OuterType4<
   LongerLongerLongerLongerInnerType extends
-    LongerLongerLongerLongerLongerLo | ngerLongerLongerOtherType,
+    | LongerLongerLongerLongerLongerLo
+    | ngerLongerLongerOtherType,
 > = { a: 1 };
 
 ================================================================================
 `;
 
+exports[`line-breaking-after-extends-2.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+a = {
+  parseFunctionBodyAndFinish<
+    T extends
+      | N.Function
+      | N.TSDeclareMethod
+      | N.TSDeclareFunction
+      | N.ClassPrivateMethod,
+  >() {}
+}
+
+function parseFunctionBodyAndFinish<
+  T extends
+    | N.Function
+    | N.TSDeclareMethod
+    | N.TSDeclareFunction
+    | N.ClassPrivateMethod
+    | Foo
+    | Bar
+    | Baz
+>();
+
+function parseFunctionBodyAndFinish<
+  T extends // comment
+    N.Function | N.TSDeclareMethod | Baz
+>();
+
+function makeChainWalker<
+  ArgT extends {
+    options: ValidatedOptions;
+    dirname: string;
+    filepath?: string;
+  },
+>() {}
+
+=====================================output=====================================
+a = {
+  parseFunctionBodyAndFinish<
+    T extends
+      | N.Function
+      | N.TSDeclareMethod
+      | N.TSDeclareFunction
+      | N.ClassPrivateMethod,
+  >() {},
+};
+
+function parseFunctionBodyAndFinish<
+  T extends
+    | N.Function
+    | N.TSDeclareMethod
+    | N.TSDeclareFunction
+    | N.ClassPrivateMethod
+    | Foo
+    | Bar
+    | Baz,
+>();
+
+function parseFunctionBodyAndFinish<
+  T extends // comment
+    N.Function | N.TSDeclareMethod | Baz,
+>();
+
+function makeChainWalker<
+  ArgT extends {
+    options: ValidatedOptions;
+    dirname: string;
+    filepath?: string;
+  },
+>() {}
+
+================================================================================
+`;
+
 exports[`long-function-arg.ts format 1`] = `
 ====================================options=====================================
 parsers: ["typescript"]
diff --git a/tests/format/typescript/typeparams/line-breaking-after-extends-2.ts b/tests/format/typescript/typeparams/line-breaking-after-extends-2.ts
new file mode 100644
--- /dev/null
+++ b/tests/format/typescript/typeparams/line-breaking-after-extends-2.ts
@@ -0,0 +1,33 @@
+a = {
+  parseFunctionBodyAndFinish<
+    T extends
+      | N.Function
+      | N.TSDeclareMethod
+      | N.TSDeclareFunction
+      | N.ClassPrivateMethod,
+  >() {}
+}
+
+function parseFunctionBodyAndFinish<
+  T extends
+    | N.Function
+    | N.TSDeclareMethod
+    | N.TSDeclareFunction
+    | N.ClassPrivateMethod
+    | Foo
+    | Bar
+    | Baz
+>();
+
+function parseFunctionBodyAndFinish<
+  T extends // comment
+    N.Function | N.TSDeclareMethod | Baz
+>();
+
+function makeChainWalker<
+  ArgT extends {
+    options: ValidatedOptions;
+    dirname: string;
+    filepath?: string;
+  },
+>() {}