Repository: prettier/prettier (JavaScript) — Bug Fix

Issue:
Unnecessary new lines injected by typescript parser.
**Prettier 1.19.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wDEII8BePAMxwHcBDbAEwCU5aAKbjbCBiJI85CAAUBQgDR5sXEZ1oAeABIAVALIAZAKIAbOAFsEMAHwBKEQCkAygA0AdAeOnqZvMAA6UPHLgwAK7YvsqsAJYAbngA9GYA3D4AvhaJUD7oWLgE0CR4AEIs1HSMLBxcvPyCwgUsktWy8rSKXGpaeoYmsJY2Ds6dbgA+eFCB+vrunj5+8kEheGFRsQnJqT4+gURw2ESORkwYvJvbIgCqW9hWeOfbk96+-nO+x9hpKSDSIIIw4bnIoCwBAxxCwEERkCAmJEIOFWB8QAAjbBMMAAawCtgwKPCUAA5sgYNhAnBPgALGBGfQAdVJ4XgRCxYDgtjBdKidIAnhCwERwZ8cRcYJImLj9shaEx9FtPgArIhofLItEYpgmbQ4uDiyXSkBytC2HG4wwARUCEHgWqlJJAWJ22whMA5GDgRDA2HCGBg8P4OJgVNhMFJyAAjAAmAAMnyqWypyIwEP4Lu2kU1nwAjmb4PUhBCmEQALRQOBwVgl+HyDPheTC0VMS06rZGcIEonWoiGk2ZzVICVWz4wJgI-2sQPIUP95HhfSGgDCECMYpQUGgqZAx3Ug-BPe11sixIAklBS7BbG6PTAAIJH2yOwz1uBJJJAA)
```sh
--parser typescript
--print-width 120
```

**Input:**
```tsx
export const Foo = forwardRef((props: FooProps, ref: Ref<HTMLElement>): JSX.Element => {
  return <div />;
});

export const Bar = forwardRef((props: BarProps, ref: Ref<HTMLElement>): JSX.Element | null => {
  return <div />;
});

users.map((user: User): User => {
  return user;
})
```

**Output:**
```tsx
export const Foo = forwardRef(
  (props: FooProps, ref: Ref<HTMLElement>): JSX.Element => {
    return <div />;
  }
);

export const Bar = forwardRef((props: BarProps, ref: Ref<HTMLElement>): JSX.Element | null => {
  return <div />;
});

users.map(
  (user: User): User => {
    return user;
  }
);

```

**Expected behavior:**
Prettier leaves the code as is.

This formatting issue only occurs when using the [typescript](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wDEII8BePAMxwHcBDbAEwCU5aAKbjbCBiJI85CAAUBQgDR5sXEZ1oAeABIAVALIAZAKIAbOAFsEMAHwBKEQCkAygA0AdAeOnqZvMAA6UPHLgwAK7YvsqsAJYAbngA9GYA3D4AvhaJUD7oWLgE0CR4AEIs1HSMLBxcvPyCwgUsktWy8rSKXGpaeoYmsJY2Ds6dbgA+eFCB+vrunj5+8kEheGFRsQnJqT4+gURw2ESORkwYvJvbIgCqW9hWeOfbk96+-nO+x9hpKSDSIIIw4bnIoCwBAxxCwEERkCAmJEIOFWB8QAAjbBMMAAawCtgwKPCUAA5sgYNhAnBPgALGBGfQAdVJ4XgRCxYDgtjBdKidIAnhCwERwZ8cRcYJImLj9shaEx9FtPgArIhofLItEYpgmbQ4uDiyXSkBytC2HG4wwARUCEHgWqlJJAWJ22whMA5GDgRDA2HCGBg8P4OJgVNhMFJyAAjAAmAAMnyqWypyIwEP4Lu2kU1nwAjmb4PUhBCmEQALRQOBwVgl+HyDPheTC0VMS06rZGcIEonWoiGk2ZzVICVWz4wJgI-2sQPIUP95HhfSGgDCECMYpQUGgqZAx3Ug-BPe11sixIAklBS7BbG6PTAAIJH2yOwz1uBJJJAA) parser. If you switch over to the [babel](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEcAeAHCAnGACSKAZ3wDEII8BePAMxwHcBDbAEwCU5aAKbjbCBiJI85CAAUBQgDR5sXEZ1oAeABIAVALIAZAKIAbOAFsEMAHwBKEQCkAygA0AdAeOnqZvMAA6UPHLgwAK7YvsqsAJYAbngA9GYA3D4AvhaJUD7oWLgE0CR4AEIs1HSMLBxcvPyCwgUsktWy8rSKXGpaeoYmsJY2Ds6dbgA+eFCB+vrunj5+8kEheGFRsQnJqT4+gURw2ESORkwYvJvbIgCqW9hWeOfbk96+-nO+x9hpKSDSIIIw4bnIoCwBAxxCwEERkCAmJEIOFWB8QAAjbBMMAAawCtgwKPCUAA5sgYNhAnBPgALGBGfQAdVJ4XgRCxYDgtjBdKidIAnhCwERwZ8cRcYJImLj9shaEx9FtPgArIhofLItEYpgmbQ4uDiyXSkBytC2HG4wwARUCEHgWqlJJAWJ22whCKYCLg+nh-BxMCpsJgpOQAEYAEwABk+VS2VORGAh-DgF0ims+AEczfB6kIIUwiABaKBwOCsfPw+TJ8LyYWipiWnVbIzhAlE61EQ0mlOapASq2fGBOr2sH3IANd5HhfSGgDCECMYpQUGgCZAx3UTvB7e11sixIAklAC7BbGBsOEMDAAII72wwDmGKtwJJJIA) parser, the code remains untouched.

The extra lines that prettier injects seem overkill, especially for the `users.map` example.

It's interesting though how that functions that have a union return type, don't get rewritten to a new line.


Discussion:
I'm not sure if this is a bug. But, it is strange that there is difference between normal return type and union return type.
If this is considered a bug, I would be happy to work on a fix
@whitneyit are you still interested in picking this one up, if no i can take it as its annoying me in my codebase
~I'm keen to work on it as well. I'm not sure if it is deemed a bug or not :man_shrugging:~

I didn't see the label come through via email :man_facepalming: 
@armano2, if you want to take a look at as well, I'm not going to say no lol

Resolving patch:
diff --git a/changelog_unreleased/typescript/10316.md b/changelog_unreleased/typescript/10316.md
new file mode 100644
index 000000000000..4ceeae64dfed
--- /dev/null
+++ b/changelog_unreleased/typescript/10316.md
@@ -0,0 +1,21 @@
+#### Allow hugging arguments that are non-concise arrow functions with return type annotations (#10316 by @thorn0)
+
+<!-- prettier-ignore -->
+```ts
+// Input
+users.map((user: User): User => {
+  return user;
+})
+
+// Prettier stable
+users.map(
+  (user: User): User => {
+    return user;
+  }
+);
+
+// Prettier main
+users.map((user: User): User => {
+  return user;
+})
+```
diff --git a/src/language-js/print/call-arguments.js b/src/language-js/print/call-arguments.js
index 42fc48035ecf..e662860ea34a 100644
--- a/src/language-js/print/call-arguments.js
+++ b/src/language-js/print/call-arguments.js
@@ -240,7 +240,9 @@ function couldGroupArg(arg) {
       // });
       (!arg.returnType ||
         !arg.returnType.typeAnnotation ||
-        arg.returnType.typeAnnotation.type !== "TSTypeReference") &&
+        arg.returnType.typeAnnotation.type !== "TSTypeReference" ||
+        // https://github.com/prettier/prettier/issues/7542
+        isNonEmptyBlockStatement(arg.body)) &&
       (arg.body.type === "BlockStatement" ||
         arg.body.type === "ArrowFunctionExpression" ||
         arg.body.type === "ObjectExpression" ||
@@ -297,4 +299,12 @@ function isReactHookCallWithDepsArray(args) {
   );
 }
 
+function isNonEmptyBlockStatement(node) {
+  return (
+    node.type === "BlockStatement" &&
+    (node.body.some((node) => node.type !== "EmptyStatement") ||
+      hasComment(node, CommentCheckFlags.Dangling))
+  );
+}
+
 module.exports = printCallArguments;


Test patch:
diff --git a/tests/typescript/typeparams/print-width-120/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/typeparams/print-width-120/__snapshots__/jsfmt.spec.js.snap
new file mode 100644
index 000000000000..666c9bb037c4
--- /dev/null
+++ b/tests/typescript/typeparams/print-width-120/__snapshots__/jsfmt.spec.js.snap
@@ -0,0 +1,51 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`issue-7542.ts - {"printWidth":120} format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 120
+                                                                                                                        | printWidth
+=====================================input======================================
+export const Foo = forwardRef((props: FooProps, ref: Ref<HTMLElement>): JSX.Element => {
+  return <div />;
+});
+
+export const Bar = forwardRef((props: BarProps, ref: Ref<HTMLElement>): JSX.Element | null => {
+  return <div />;
+});
+
+users.map((user: User): User => {
+  return user;
+})
+
+users.map((user: User): User => {
+  ; // comment
+})
+
+users.map((user: User): User => {
+  // comment
+})
+
+=====================================output=====================================
+export const Foo = forwardRef((props: FooProps, ref: Ref<HTMLElement>): JSX.Element => {
+  return <div />;
+});
+
+export const Bar = forwardRef((props: BarProps, ref: Ref<HTMLElement>): JSX.Element | null => {
+  return <div />;
+});
+
+users.map((user: User): User => {
+  return user;
+});
+
+users.map((user: User): User => {
+  // comment
+});
+
+users.map((user: User): User => {
+  // comment
+});
+
+================================================================================
+`;
diff --git a/tests/typescript/typeparams/print-width-120/issue-7542.ts b/tests/typescript/typeparams/print-width-120/issue-7542.ts
new file mode 100644
index 000000000000..b340165235cd
--- /dev/null
+++ b/tests/typescript/typeparams/print-width-120/issue-7542.ts
@@ -0,0 +1,19 @@
+export const Foo = forwardRef((props: FooProps, ref: Ref<HTMLElement>): JSX.Element => {
+  return <div />;
+});
+
+export const Bar = forwardRef((props: BarProps, ref: Ref<HTMLElement>): JSX.Element | null => {
+  return <div />;
+});
+
+users.map((user: User): User => {
+  return user;
+})
+
+users.map((user: User): User => {
+  ; // comment
+})
+
+users.map((user: User): User => {
+  // comment
+})
diff --git a/tests/typescript/typeparams/print-width-120/jsfmt.spec.js b/tests/typescript/typeparams/print-width-120/jsfmt.spec.js
new file mode 100644
index 000000000000..fabcb10c9c8d
--- /dev/null
+++ b/tests/typescript/typeparams/print-width-120/jsfmt.spec.js
@@ -0,0 +1,1 @@
+run_spec(__dirname, ["typescript"], { printWidth: 120 });