Repository: prettier/prettier (JavaScript)

Issue:
`declare function` formatted differently for Flow and TypeScript
**Prettier 2.7.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEATOYA2BDATnAAgDMBXKMGAS2gICMcCAKAB1wmYGckDgDtvaECJjjYoAGjrcOMXJSgBzSWG5QSAW1pxcBAL4BKJAB0oBAgAU26yhzgAeAIK5c2AJ52iQgHxeQ4kOxU0BzIoHhsAO7meAghKNiYEW4h-rQuYADWcDAAytjqcAAy8nDIRAm2qelZuczYYPIKyLIkcP5wmnCo6KiFYgok2ApwAGIQuOrYMFSKyCDYJDAQfiAAFjDqmADqq5TwHHVgcDmxe5QAbnuuc2AcKSDytrgwlkOTZRVtIABWHAAeOUaIgAiiQIPAPphKiA6rgnnMYK5mHAOGA5MwYCtWPIYFtKKgYKtkAAOAAM-lYEFsWxczDmrBR2nOpX8AEcwfBLOw4vMOABaKBwLpdFb4dmUfCvBTvJDlKFfWzWZq4Vr+DhAuCg8GlWWffwwbC0PEEolIABM+pclEwjQAwhB1DKQCiAKwrEi2AAqhricuh51aAEkoOhYDk0ZQMQ4QzlESJIbZdLogA)
<!-- prettier-ignore -->
```sh
--parser typescript
```

**Input:**
<!-- prettier-ignore -->
```tsx
declare function bla (props: { a: boolean, b: string, c: number }):
  Promise<Array<foo>>
```

**Output:**
<!-- prettier-ignore -->
```tsx
declare function bla(props: {
  a: boolean;
  b: string;
  c: number;
}): Promise<Array<foo>>;

```

**Prettier 2.7.1**
[Playground link](https://prettier.io/playground/#N4Igxg9gdgLgprEAuEATOYA2BDATnAAgDMBXKMGAS2gICMcCAKAB1wmYGckDgDtvaECJjjYoAGjrcOMXJSgBzSWG5QSAW1pxcBAL4BKJAB0oBAgAU26yhzgAeAIK5c2AJ52iQgHxeQ4kOxU0BzIoHhsAO7meAghKNiYEW4h-rQuYADWcDAAytjqcAAy8nDIRAm2qelZuczYYPIKyLIkcP5wmnCo6KiFYgok2ApwAGIQuOrYMFSKyCDYJDAQfiAAFjDqmADqq5TwHHVgcDmxe5QAbnuuc2AcKSDytrgwlkOTZRVtIABWHAAeOUaIgAiiQIPAPphKiA6rgnnMiJgIBEVqx5DAtpRUDBVsgABwABn8rAgti2LmYc1YcCe51K-gAjmD4JZ2HF5hwALRQOBdLorfBMyj4V4Kd5IcpQr62azNXCtfwcIFwUHg0oSz7+GDYWiY7G4pAAJi1LkomEaAGEIOpxSAaQBWFYkWwAFR1cUl0POrQAklB0LAcmA5MwYA5-TkYK4RJDbLpdEA)
<!-- prettier-ignore -->
```sh
--parser flow
```

**Output:**
<!-- prettier-ignore -->
```jsx
declare function bla(props: { a: boolean, b: string, c: number }): Promise<
  Array<foo>
>;

```

**Expected behavior:**

The formatting used for TS ("the object type breaks before the return type") should be use for Flow too.
Related: https://github.com/prettier/prettier/pull/13393#issuecomment-1229540128


Hints:

Patch:
diff --git a/src/language-js/print/function-parameters.js b/src/language-js/print/function-parameters.js
--- a/src/language-js/print/function-parameters.js
+++ b/src/language-js/print/function-parameters.js
@@ -62,7 +62,7 @@ function printFunctionParameters(
 
   const parent = path.getParentNode();
   const isParametersInTestCall = isTestCall(parent);
-  const shouldHugParameters = shouldHugFunctionParameters(functionNode);
+  const shouldHugParameters = shouldHugTheOnlyFunctionParameter(functionNode);
   const printed = [];
   iterateFunctionParametersPath(path, (parameterPath, index) => {
     const isLastParameter = index === parameters.length - 1;
@@ -157,7 +157,7 @@ function printFunctionParameters(
   ];
 }
 
-function shouldHugFunctionParameters(node) {
+function shouldHugTheOnlyFunctionParameter(node) {
   if (!node) {
     return false;
   }
@@ -176,7 +176,8 @@ function shouldHugFunctionParameters(node) {
           parameter.typeAnnotation.type === "TSTypeAnnotation") &&
         isObjectType(parameter.typeAnnotation.typeAnnotation)) ||
       (parameter.type === "FunctionTypeParam" &&
-        isObjectType(parameter.typeAnnotation)) ||
+        isObjectType(parameter.typeAnnotation) &&
+        parameter !== node.rest) ||
       (parameter.type === "AssignmentPattern" &&
         (parameter.left.type === "ObjectPattern" ||
           parameter.left.type === "ArrayPattern") &&
@@ -283,6 +284,6 @@ function isDecoratedFunction(path) {
 
 export {
   printFunctionParameters,
-  shouldHugFunctionParameters,
+  shouldHugTheOnlyFunctionParameter,
   shouldGroupFunctionParameters,
 };
diff --git a/src/language-js/print/object.js b/src/language-js/print/object.js
--- a/src/language-js/print/object.js
+++ b/src/language-js/print/object.js
@@ -19,12 +19,12 @@ import {
   getComments,
   CommentCheckFlags,
   isNextLineEmpty,
+  isObjectType,
 } from "../utils/index.js";
 import { locStart, locEnd } from "../loc.js";
 
 import { printOptionalToken, printTypeAnnotation } from "./misc.js";
-import { shouldHugFunctionParameters } from "./function-parameters.js";
-import { shouldHugType } from "./type-annotation.js";
+import { shouldHugTheOnlyFunctionParameter } from "./function-parameters.js";
 import { printHardlineAfterHeritage } from "./class.js";
 
 /** @typedef {import("../../document/builders.js").Doc} Doc */
@@ -201,26 +201,21 @@ function printObject(path, options, print) {
   if (
     path.match(
       (node) => node.type === "ObjectPattern" && !node.decorators,
-      (node, name, number) =>
-        shouldHugFunctionParameters(node) &&
-        (name === "params" ||
-          name === "parameters" ||
-          name === "this" ||
-          name === "rest") &&
-        number === 0
-    ) ||
-    path.match(
-      shouldHugType,
-      (node, name) => name === "typeAnnotation",
-      (node, name) => name === "typeAnnotation",
-      (node, name, number) =>
-        shouldHugFunctionParameters(node) &&
-        (name === "params" ||
-          name === "parameters" ||
-          name === "this" ||
-          name === "rest") &&
-        number === 0
+      shouldHugTheOnlyParameter
     ) ||
+    (isObjectType(node) &&
+      (path.match(
+        undefined,
+        (node, name) => name === "typeAnnotation",
+        (node, name) => name === "typeAnnotation",
+        shouldHugTheOnlyParameter
+      ) ||
+        path.match(
+          undefined,
+          (node, name) =>
+            node.type === "FunctionTypeParam" && name === "typeAnnotation",
+          shouldHugTheOnlyParameter
+        ))) ||
     // Assignment printing logic (printAssignment) is responsible
     // for adding a group if needed
     (!shouldBreak &&
@@ -237,4 +232,14 @@ function printObject(path, options, print) {
   return group(content, { shouldBreak });
 }
 
+function shouldHugTheOnlyParameter(node, name) {
+  return (
+    (name === "params" ||
+      name === "parameters" ||
+      name === "this" ||
+      name === "rest") &&
+    shouldHugTheOnlyFunctionParameter(node)
+  );
+}
+
 export { printObject };


Test patch:
diff --git a/tests/format/flow/function-single-destructuring/__snapshots__/jsfmt.spec.js.snap b/tests/format/flow/function-single-destructuring/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/flow/function-single-destructuring/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/flow/function-single-destructuring/__snapshots__/jsfmt.spec.js.snap
@@ -133,3 +133,45 @@ const X = (props: { a: boolean }) => <A />;
 
 ================================================================================
 `;
+
+exports[`object-type-in-declare-function.js format 1`] = `
+====================================options=====================================
+parsers: ["flow", "babel"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+declare function foo(this: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bazFlip({ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...{ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...x: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+=====================================output=====================================
+declare function foo(this: {
+  a: boolean,
+  b: string,
+  c: number,
+}): Promise<Array<foo>>;
+
+declare function bazFlip({
+  a: boolean,
+  b: string,
+  c: number,
+}): Promise<Array<foo>>;
+
+declare function bar(
+  ...{ a: boolean, b: string, c: number }
+): Promise<Array<foo>>;
+
+declare function bar(
+  ...x: { a: boolean, b: string, c: number }
+): Promise<Array<foo>>;
+
+================================================================================
+`;
diff --git a/tests/format/flow/function-single-destructuring/object-type-in-declare-function.js b/tests/format/flow/function-single-destructuring/object-type-in-declare-function.js
new file mode 100644
--- /dev/null
+++ b/tests/format/flow/function-single-destructuring/object-type-in-declare-function.js
@@ -0,0 +1,11 @@
+declare function foo(this: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bazFlip({ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...{ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...x: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
diff --git a/tests/format/typescript/declare/__snapshots__/jsfmt.spec.js.snap b/tests/format/typescript/declare/__snapshots__/jsfmt.spec.js.snap
--- a/tests/format/typescript/declare/__snapshots__/jsfmt.spec.js.snap
+++ b/tests/format/typescript/declare/__snapshots__/jsfmt.spec.js.snap
@@ -215,3 +215,45 @@ class C {
 
 ================================================================================
 `;
+
+exports[`object-type-in-declare-function.ts format 1`] = `
+====================================options=====================================
+parsers: ["typescript"]
+printWidth: 80
+                                                                                | printWidth
+=====================================input======================================
+declare function foo(this: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bazFlip({ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...{ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...x: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+=====================================output=====================================
+declare function foo(this: {
+  a: boolean;
+  b: string;
+  c: number;
+}): Promise<Array<foo>>;
+
+declare function bazFlip({
+  a: boolean,
+  b: string,
+  c: number,
+}): Promise<Array<foo>>;
+
+declare function bar(
+  ...{ a: boolean, b: string, c: number }
+): Promise<Array<foo>>;
+
+declare function bar(
+  ...x: { a: boolean; b: string; c: number }
+): Promise<Array<foo>>;
+
+================================================================================
+`;
diff --git a/tests/format/typescript/declare/object-type-in-declare-function.ts b/tests/format/typescript/declare/object-type-in-declare-function.ts
new file mode 100644
--- /dev/null
+++ b/tests/format/typescript/declare/object-type-in-declare-function.ts
@@ -0,0 +1,11 @@
+declare function foo(this: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bazFlip({ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...{ a: boolean, b: string, c: number }):
+  Promise<Array<foo>>
+
+declare function bar(...x: { a: boolean, b: string, c: number }):
+  Promise<Array<foo>>