diff --git a/crates/biome_js_analyze/src/analyzers/style/use_shorthand_function_type.rs b/crates/biome_js_analyze/src/analyzers/style/use_shorthand_function_type.rs
index 6112f90efeb7..a1f180a40022 100644
--- a/crates/biome_js_analyze/src/analyzers/style/use_shorthand_function_type.rs
+++ b/crates/biome_js_analyze/src/analyzers/style/use_shorthand_function_type.rs
@@ -145,7 +145,8 @@ impl Rule for UseShorthandFunctionType {
node,
)?),
)
- .build();
+ .build()
+ .with_type_parameters(interface_decl.type_parameters());
mutation.replace_node(
AnyJsDeclarationClause::from(interface_decl),
diff --git a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_jsx_key_in_iterable.rs b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_jsx_key_in_iterable.rs
index a4b83e9c6a70..3824d8f370df 100644
--- a/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_jsx_key_in_iterable.rs
+++ b/crates/biome_js_analyze/src/semantic_analyzers/nursery/use_jsx_key_in_iterable.rs
@@ -206,7 +206,14 @@ fn handle_iterators(
let body = callback.body().ok()?;
match body {
AnyJsFunctionBody::AnyJsExpression(expr) => {
- handle_potential_react_component(&expr, model, is_inside_jsx)
+ // unwrap parenthesized expression
+ let mut inner_expr = expr;
+ while let AnyJsExpression::JsParenthesizedExpression(parenthesized_expr) =
+ inner_expr
+ {
+ inner_expr = parenthesized_expr.expression().ok()?;
+ }
+ handle_potential_react_component(&inner_expr, model, is_inside_jsx)
.map(|state| vec![state])
}
AnyJsFunctionBody::JsFunctionBody(body) => {
diff --git a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx
index cf84322cb0c6..6a26c119f7ab 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx
+++ b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx
@@ -35,3 +35,5 @@ React.Children.map(c => React.cloneElement(c));
(
{data.map(c => )}
)
({data.map(c => xyz)}
)
+
+({data.map(c => ())}
)
\ No newline at end of file
diff --git a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx.snap
index f6930686c0a6..2f1129760362 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx.snap
+++ b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/invalid.jsx.snap
@@ -42,6 +42,7 @@ React.Children.map(c => React.cloneElement(c));
({data.map(c => xyz)}
)
+({data.map(c => ())}
)
```
# Diagnostics
@@ -586,6 +587,7 @@ invalid.jsx:37:21 lint/nursery/useJsxKeyInIterable ━━━━━━━━━
> 37 │ ({data.map(c => xyz)}
)
│ ^^^
38 │
+ 39 │ ({data.map(c => ())}
)
i Either return a JSX expression, or suppress this instance if you determine it is safe.
@@ -593,3 +595,20 @@ invalid.jsx:37:21 lint/nursery/useJsxKeyInIterable ━━━━━━━━━
```
+
+```
+invalid.jsx:39:22 lint/nursery/useJsxKeyInIterable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! Missing key property for this element in iterable.
+
+ 37 │ ({data.map(c => xyz)}
)
+ 38 │
+ > 39 │ ({data.map(c => ())}
)
+ │ ^^^^
+
+ i The order of the items may change, and having a key can help React identify which item was moved.
+
+ i Check the React documentation.
+
+
+```
diff --git a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx
index d9088a1e5006..c657aca06d9c 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx
+++ b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx
@@ -42,3 +42,5 @@ React.Children.map(c => React.cloneElement(c, {key: c}));
({[, , ]}
)
({data.map(c => )}
)
+
+({data.map(c => ())}
)
\ No newline at end of file
diff --git a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx.snap b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx.snap
index a5a0830aadc6..6cbcb18b389a 100644
--- a/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx.snap
+++ b/crates/biome_js_analyze/tests/specs/nursery/useJsxKeyInIterable/valid.jsx.snap
@@ -49,4 +49,5 @@ React.Children.map(c => React.cloneElement(c, {key: c}));
({data.map(c => )}
)
+({data.map(c => ())}
)
```
diff --git a/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts b/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts
index 92a27cb65df2..e8f9fcd63508 100644
--- a/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts
+++ b/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts
@@ -31,3 +31,8 @@ type GenericCallSignature = { (arg: T): T };
// Object type with optional call signature
let optionalCall: { (): number | undefined };
+
+// Generic interface with a call signature
+interface GenericInterface {
+ (value: T): boolean;
+}
diff --git a/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts.snap b/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts.snap
index 9fec550677d4..84f235718988 100644
--- a/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts.snap
+++ b/crates/biome_js_analyze/tests/specs/style/useShorthandFunctionType/invalid.ts.snap
@@ -38,6 +38,11 @@ type GenericCallSignature = { (arg: T): T };
// Object type with optional call signature
let optionalCall: { (): number | undefined };
+// Generic interface with a call signature
+interface GenericInterface {
+ (value: T): boolean;
+}
+
```
# Diagnostics
@@ -229,6 +234,7 @@ invalid.ts:33:21 lint/style/useShorthandFunctionType FIXABLE ━━━━━
> 33 │ let optionalCall: { (): number | undefined };
│ ^^^^^^^^^^^^^^^^^^^^^^
34 │
+ 35 │ // Generic interface with a call signature
i Types containing only a call signature can be shortened to a function type.
@@ -239,8 +245,34 @@ invalid.ts:33:21 lint/style/useShorthandFunctionType FIXABLE ━━━━━
33 │ - let·optionalCall:·{·():·number·|·undefined·};
33 │ + let·optionalCall:·()·=>·number·|·undefined;
34 34 │
+ 35 35 │ // Generic interface with a call signature
```
+```
+invalid.ts:37:2 lint/style/useShorthandFunctionType FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
+
+ ! Use a function type instead of a call signature.
+
+ 35 │ // Generic interface with a call signature
+ 36 │ interface GenericInterface {
+ > 37 │ (value: T): boolean;
+ │ ^^^^^^^^^^^^^^^^^^^^
+ 38 │ }
+ 39 │
+
+ i Types containing only a call signature can be shortened to a function type.
+
+ i Safe fix: Alias a function type instead of using an interface with a call signature.
+
+ 34 34 │
+ 35 35 │ // Generic interface with a call signature
+ 36 │ - interface·GenericInterface·{
+ 37 │ - → (value:·T):·boolean;
+ 38 │ - }
+ 36 │ + type·GenericInterface·=·(value:·T)·=>·boolean
+ 39 37 │
+
+```
diff --git a/website/src/assets/vscode-extension.png b/website/src/assets/vscode-extension.png
index 2ef4ba3dd6fe..06f6e7deb39a 100644
Binary files a/website/src/assets/vscode-extension.png and b/website/src/assets/vscode-extension.png differ