Repository: prettier/prettier (JavaScript)
Date: 2021-05-16 15:13:38+00:00
Task type: Bug Fix

Issue:
Ugly formatting for complex type parameters
**Prettier 2.3.0**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEMCeAHOACAYgSzgBsATAGQEM0IBXGAdXxgAsAeAHSm2wBVs4AHvCgkAztlEwATvigBzADSduAZX5CEY7DSgBrKBADuXALzZg2Q-hIskE6bLnYAvpwB82M8GXZ0WOzwA3D6QJHB2kjLywVwS+ABe4dgqMc4xIAogEBgw+NCiyKAUUlJGAArFCAUoFESGVAWZAEZSFGC6cDAqGG2OyNI0cJlwALZNcCRh5BTyNBRycLgQUiMUMLnyyCAUdBAZIMwwI0T0zExwoj1gcCpVTPgAbkxoW2CijSCyonBSMGWtclWyAAZrVvpkAFaiAQAIVa7U6KgoIzgZFkcBBYKGIChAhUjiIcAAijQIPBMURwSAelJvlItn4LmAZDl9hgogxrCxkAAOAAMmXZEG+9FaGC27IuPweGMyAEdSfB-tlqttRABaKBwCYTfZSOAK-D6-7zIFIUGU7HfEb4fpSQaZUQE4mKjHmrGZGAUJqMGzMZAAJk9rXwREcAGEICMzSALgBWfY0b48b3VC1Uh6DACSIgQXWZ+ByAEERCp0ISKd9nM4gA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
type FieldLayoutWith<
  T extends string,
  S extends unknown = { width: string }
> = {
  type: T;
  code: string;
  size: S;
};

```

**Output:**
<!-- prettier-ignore -->
```tsx
type FieldLayoutWith<T extends string, S extends unknown = { width: string }> =
  {
    type: T;
    code: string;
    size: S;
  };

```

**Expected behavior:**
Same as input:
```tsx
type FieldLayoutWith<
  T extends string,
  S extends unknown = { width: string }
> = {
  type: T;
  code: string;
  size: S;
};

```

Feedback from https://github.com/kintone/js-sdk/pull/873/files#diff-842638d7176db5a04f5cd308d236c51206ffb30ed37ab3ffd0c93700ca5876cbR1-R6. Similar to #10850.


Issue discussion:

Solution patch:
diff --git a/changelog_unreleased/typescript/10901.md b/changelog_unreleased/typescript/10901.md
new file mode 100644
index 000000000000..4ab300e845e9
--- /dev/null
+++ b/changelog_unreleased/typescript/10901.md
@@ -0,0 +1,33 @@
+#### Break the LHS of type alias that has complex type parameters (#10901 by @sosukesusuzki)
+
+<!-- prettier-ignore -->
+```ts
+// Input
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+// Prettier stable
+type FieldLayoutWith<T extends string, S extends unknown = { width: string }> =
+  {
+    type: T;
+    code: string;
+    size: S;
+  };
+
+// Prettier main
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+```
diff --git a/src/language-js/print/assignment.js b/src/language-js/print/assignment.js
index ebc055ecb137..e9f4d910c224 100644
--- a/src/language-js/print/assignment.js
+++ b/src/language-js/print/assignment.js
@@ -136,7 +136,7 @@ function chooseLayout(path, options, print, leftDoc, rightPropertyName) {
     return "never-break-after-operator";
   }
 
-  if (isComplexDestructuring(node)) {
+  if (isComplexDestructuring(node) || isComplexTypeAliasParams(node)) {
     return "break-lhs";
   }
 
@@ -243,6 +243,32 @@ function isAssignmentOrVariableDeclarator(node) {
   return isAssignment(node) || node.type === "VariableDeclarator";
 }
 
+function isComplexTypeAliasParams(node) {
+  const typeParams = getTypeParametersFromTypeAlias(node);
+  if (isNonEmptyArray(typeParams)) {
+    const constraintPropertyName =
+      node.type === "TSTypeAliasDeclaration" ? "constraint" : "bound";
+    if (
+      typeParams.length > 1 &&
+      typeParams.some((param) => param[constraintPropertyName] || param.default)
+    ) {
+      return true;
+    }
+  }
+  return false;
+}
+
+function getTypeParametersFromTypeAlias(node) {
+  if (isTypeAlias(node) && node.typeParameters && node.typeParameters.params) {
+    return node.typeParameters.params;
+  }
+  return null;
+}
+
+function isTypeAlias(node) {
+  return node.type === "TSTypeAliasDeclaration" || node.type === "TypeAlias";
+}
+
 /**
  * A chain with no calls at all or whose calls are all without arguments or with lone short arguments,
  * excluding chains printed by `printMemberChain`


Test patch:
diff --git a/tests/format/flow/type-alias/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/type-alias/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..34530f271b4f
--- /dev/null
+++ b/tests/format/flow/type-alias/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,76 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`issue-100857.js format 1`] = `
+====================================options=====================================
+parsers: ["flow", "babel-flow"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+type FieldLayoutWith<
+  T : string,
+  S : unknown = { xxxxxxxx: number; y: string; }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : string,
+  S : unknown,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : string,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : stringgggggggggggggggggg,
+  S : stringgggggggggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+=====================================output=====================================
+type FieldLayoutWith<
+  T: string,
+  S: unknown = { xxxxxxxx: number, y: string }
+> = {
+  type: T,
+  code: string,
+  size: S,
+};
+
+type FieldLayoutWith<T: string, S: unknown> = {
+  type: T,
+  code: string,
+  size: S,
+};
+
+type FieldLayoutWith<T: string> = {
+  type: T,
+  code: string,
+  size: S,
+};
+
+type FieldLayoutWith<
+  T: stringgggggggggggggggggg,
+  S: stringgggggggggggggggggg
+> = {
+  type: T,
+  code: string,
+  size: S,
+};
+
+================================================================================
+`;
diff --git a/tests/format/flow/type-alias/issue-100857.js b/tests/format/flow/type-alias/issue-100857.js
new file mode 100644
index 000000000000..a9d7b04ea526
--- /dev/null
+++ b/tests/format/flow/type-alias/issue-100857.js
@@ -0,0 +1,34 @@
+type FieldLayoutWith<
+  T : string,
+  S : unknown = { xxxxxxxx: number; y: string; }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : string,
+  S : unknown,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : string,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T : stringgggggggggggggggggg,
+  S : stringgggggggggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
diff --git a/tests/format/flow/type-alias/jsfmt.spec.js b/tests/format/flow/type-alias/jsfmt.spec.js
new file mode 100644
index 000000000000..f5bd7f6bdcd0
--- /dev/null
+++ b/tests/format/flow/type-alias/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["flow", "babel-flow"]);
diff --git a/tests/format/typescript/type-alias/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/type-alias/__snapshots__/jsfmt.spec.js.snap
index 6f10128eab38..9da037d1066c 100644
--- a/tests/format/typescript/type-alias/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/type-alias/__snapshots__/jsfmt.spec.js.snap
@@ -15,6 +15,99 @@ export type RequestNextDealAction =
 ================================================================================
 `;
 
+exports[`issue-100857.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  T extends stringggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  S = stringggggggggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+=====================================output=====================================
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<T extends string, S extends unknown> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<S extends unknown = { width: string }> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  T extends stringggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  S = stringggggggggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+================================================================================
+`;
+
 exports[`pattern-parameter.ts format 1`] = `
 ====================================options=====================================
 parsers: ["typescript"]
diff --git a/tests/format/typescript/type-alias/issue-100857.ts b/tests/format/typescript/type-alias/issue-100857.ts
new file mode 100644
index 000000000000..7c2a3144bee6
--- /dev/null
+++ b/tests/format/typescript/type-alias/issue-100857.ts
@@ -0,0 +1,43 @@
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends string,
+  S extends unknown,
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  S extends unknown = { width: string }
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  T extends stringggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};
+
+type FieldLayoutWith<
+  T extends stringggggggggggg,
+  S = stringggggggggggggggggg
+> = {
+  type: T;
+  code: string;
+  size: S;
+};