Repository: biomejs/biome (language: rust)

Issue:
Biome seems to handle lineWidth differently than Prettier
### Environment information

```bash
Playground provided below.
```


### Configuration

```JSON
Playground provided below.
```


### Playground link

https://biomejs.dev/playground/?lineWidth=100&indentStyle=space&trailingComma=es5&arrowParentheses=as-needed&code=ZQB4AHAAbwByAHQAIABjAG8AbgBzAHQAIAB1AHMAZQBCAGUAZgBvAHIAZQBVAG4AbABvAGEAZAAgAD0AIAAoAHsACgAgACAAdwBoAGUAbgAsAAoAIAAgAG0AZQBzAHMAYQBnAGUALAAKAH0AOgAgAHsACgAgACAAdwBoAGUAbgA6ACAAYgBvAG8AbABlAGEAbgA7AAoAIAAgAG0AZQBzAHMAYQBnAGUAOgAgAHMAdAByAGkAbgBnADsACgB9ACkAIAA9AD4AIAB7AAoAIAAgAGMAbwBuAHMAbwBsAGUALgBsAG8AZwAoACcALgAuAC4ALgAuAC4AJwApADsACgB9AA%3D%3D

### Code of Conduct

- [X] I agree to follow Biome's Code of Conduct


Discussion / hints:
![image](https://github.com/biomejs/biome/assets/28648715/6700638b-510f-4445-9581-e2605f78f72a)

Can I know why for this or it's intentional design?
I think this is a formatter bug.
When `{` and `when` are not on the same line, the group `{ when: boolean, message: string }` will be forced to break ([playground](https://biomejs.dev/playground/?lineWidth=100&indentStyle=space&trailingComma=es5&arrowParentheses=as-needed&code=YwBvAG4AcwB0ACAAZgBvAG8AIAA9ACAAKAB7AGEAfQA6ACAAewAKACAAIABhADoAbgB1AG0AYgBlAHIAfQApACAAPQA%2BACAAewB9AAoACgBjAG8AbgBzAHQAIABiAGEAcgAgAD0AIAAoAHsAYQB9ADoAIAB7AGEAOgBuAHUAbQBiAGUAcgB9ACkAIAA9AD4AIAB7AH0ACgA%3D)).

This mechanism would make the following case more readable from my perspective:

```jsx
// input
const Comp = (props: {
  a:number, b:number, c:number, d:number, }) => {
  return <div/>
}

// biome
const Comp = (props: {
  a: number;
  b: number;
  c: number;
  d: number;
}) => {
  return <div />;
};

// prettier 
const Comp = (props: { a: number; b: number; c: number; d: number }) => {
  return <div />;
};
```
Not sure if it's a bug with biome, even though it's inconsistent with prettier.
Both Biome and Prettier has a heustric that object literals are break down to multiple lines if the first property of the object has a leading new line (See https://prettier.io/docs/rationale#multi-line-objects). However, Biome does this heustric in object literal types and binding patterns, while Prettier does only in object literal **values**. This causes the inconsistency between Biome and Prettier.

FYI, recently Prettier added the `objectWrap` option in v3.5 (see [their blog](https://prettier.io/blog/2025/02/09/3.5.0)), which enforces object literals to be collapsed if possible. We followed that for Biome v2.0: [`objectWrap`](https://deploy-preview-1517--biomejs.netlify.app/reference/configuration/#javascriptformatterobjectwrap), but not yet released.

Fix patch:
diff --git a/.changeset/cyan-beans-yawn.md b/.changeset/cyan-beans-yawn.md
new file mode 100644
index 000000000000..4ae030894ef7
--- /dev/null
+++ b/.changeset/cyan-beans-yawn.md
@@ -0,0 +1,5 @@
+---
+"@biomejs/biome": patch
+---
+
+Fix [#2406](https://github.com/biomejs/biome/issues/2406), don't expand properties of object type annotation in function parameters, which was inconsistent with Prettier.
diff --git a/crates/biome_js_formatter/src/utils/object_like.rs b/crates/biome_js_formatter/src/utils/object_like.rs
index 1f1df1f4931f..2c17365fcd5e 100644
--- a/crates/biome_js_formatter/src/utils/object_like.rs
+++ b/crates/biome_js_formatter/src/utils/object_like.rs
@@ -2,7 +2,9 @@ use crate::prelude::*;
 use crate::JsFormatContext;
 use biome_formatter::write;
 use biome_formatter::{Format, FormatResult};
-use biome_js_syntax::{JsObjectExpression, JsSyntaxToken, TsObjectType};
+use biome_js_syntax::{
+    JsFormalParameter, JsObjectExpression, JsSyntaxToken, TsObjectType, TsTypeAnnotation,
+};
 use biome_rowan::{declare_node_union, AstNode, AstNodeList, AstSeparatedList, SyntaxResult};
 
 declare_node_union! {
@@ -61,8 +63,13 @@ impl Format<JsFormatContext> for JsObjectLike {
             )?;
         } else {
             let should_insert_space_around_brackets = f.options().bracket_spacing().value();
-            let should_expand =
-                f.options().object_wrap().is_preserve() && self.members_have_leading_newline();
+            let should_expand = f.options().object_wrap().is_preserve()
+                && self.members_have_leading_newline()
+                // const fn = ({ foo }: { foo: string }) => { ... };
+                //                      ^ do not break properties here
+                && self
+                    .parent::<TsTypeAnnotation>()
+                    .is_none_or(|node| node.parent::<JsFormalParameter>().is_none());
 
             write!(
                 f,


Test patch:
diff --git a/crates/biome_js_formatter/tests/specs/ts/object/objects.ts b/crates/biome_js_formatter/tests/specs/ts/object/objects.ts
new file mode 100644
index 000000000000..6d342c484653
--- /dev/null
+++ b/crates/biome_js_formatter/tests/specs/ts/object/objects.ts
@@ -0,0 +1,19 @@
+let foo: {
+	bar: string;
+};
+
+const fn = ({
+	foo,
+	bar,
+}: {
+	foo: boolean;
+	bar: string;
+}) => {
+	console.log(foo, bar);
+}
+
+function fn2(foo: {
+	bar: string;
+}) {
+	console.log(foo);
+}
diff --git a/crates/biome_js_formatter/tests/specs/ts/object/objects.ts.snap b/crates/biome_js_formatter/tests/specs/ts/object/objects.ts.snap
new file mode 100644
index 000000000000..a7871a528312
--- /dev/null
+++ b/crates/biome_js_formatter/tests/specs/ts/object/objects.ts.snap
@@ -0,0 +1,66 @@
+---
+source: crates/biome_formatter_test/src/snapshot_builder.rs
+info: ts/object/objects.ts
+---
+# Input
+
+```ts
+let foo: {
+	bar: string;
+};
+
+const fn = ({
+	foo,
+	bar,
+}: {
+	foo: boolean;
+	bar: string;
+}) => {
+	console.log(foo, bar);
+}
+
+function fn2(foo: {
+	bar: string;
+}) {
+	console.log(foo);
+}
+
+```
+
+
+=============================
+
+# Outputs
+
+## Output 1
+
+-----
+Indent style: Tab
+Indent width: 2
+Line ending: LF
+Line width: 80
+Quote style: Double Quotes
+JSX quote style: Double Quotes
+Quote properties: As needed
+Trailing commas: All
+Semicolons: Always
+Arrow parentheses: Always
+Bracket spacing: true
+Bracket same line: false
+Attribute Position: Auto
+Object wrap: Preserve
+-----
+
+```ts
+let foo: {
+	bar: string;
+};
+
+const fn = ({ foo, bar }: { foo: boolean; bar: string }) => {
+	console.log(foo, bar);
+};
+
+function fn2(foo: { bar: string }) {
+	console.log(foo);
+}
+```