Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/big-keys-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Fixed [#7381](https://github.com/biomejs/biome/issues/7381), now the [`useOptionalChain`](https://biomejs.dev/ja/linter/rules/use-optional-chain/) rule recognizes optional chaining using Yoda expressions (e.g., `undefined !== foo && foo.bar`).
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,10 @@ impl Rule for UseOptionalChain {
/// Normalize optional chain like.
/// E.g. `foo != null` is normalized to `foo`
fn normalized_optional_chain_like(expression: AnyJsExpression) -> SyntaxResult<AnyJsExpression> {
if let AnyJsExpression::JsBinaryExpression(expression) = &expression
&& expression.is_optional_chain_like()?
if let AnyJsExpression::JsBinaryExpression(binary_expression) = &expression
&& let Some(expr) = binary_expression.extract_optional_chain_like()?
{
return expression.left();
return Ok(expr);
}
Ok(expression)
}
Expand Down Expand Up @@ -561,10 +561,9 @@ impl LogicalAndChain {
// ```js
// foo && foo.bar;
// ```
AnyJsExpression::JsBinaryExpression(expression) => expression
.is_optional_chain_like()
.ok()?
.then_some(expression.left().ok()?)?,
AnyJsExpression::JsBinaryExpression(expression) => {
expression.extract_optional_chain_like().ok()??
}
expression => expression,
};
let head = match expression {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// chained calls
foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong
foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong
foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong

// case with a jump (i.e. a non-nullish prop)
foo && foo.bar && foo.bar.baz.buzz() && bing.bong
foo.bar && foo.bar.baz.buzz() && bing.bong

// case with a jump (i.e. a non-nullish prop)
foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong

// case with a call expr inside the chain for some inefficient reason
foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong

// chained calls with element access
foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong
foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong

// (partially) pre-optional chained
foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong
foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong
foo && foo?.() && foo?.().bar && bing.bong
foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong

// strict nullish equality checks null !== x && null !== x.y
// chained members
null !== foo && foo.bar
null !== foo.bar && foo.bar.baz
null !== foo && foo()
null !== foo.bar && foo.bar()
null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz
null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz

// case with a jump (i.e. a non-nullish prop)
null !== foo && null !== foo.bar && foo.bar.baz.buzz
null !== foo.bar && foo.bar.baz.buzz

// case where for some reason there is a doubled up expression
null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz
null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz && foo.bar.baz.buzz

// chained members with element access
null !== foo && null !== foo[bar] && null !== foo[bar].baz && foo[bar].baz.buzz

// case with a jump (i.e. a non-nullish prop)
null !== foo && null !== foo[bar].baz && foo[bar].baz.buzz

// chained calls
null !== foo && null !== foo.bar && null !== foo.bar.baz && foo.bar.baz.buzz()
null !== foo && null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz()
null !== foo.bar && null !== foo.bar.baz && null !== foo.bar.baz.buzz && foo.bar.baz.buzz()

// case with a jump (i.e. a non-nullish prop)
null !== foo && null !== foo.bar && foo.bar.baz.buzz()
null !== foo.bar && foo.bar.baz.buzz()

// case with a jump (i.e. a non-nullish prop)
null !== foo && null !== foo.bar && null !== foo.bar.baz.buzz && foo.bar.baz.buzz()

// case with a call expr inside the chain for some inefficient reason
null !== foo && null !== foo.bar() && null !== foo.bar().baz && null !== foo.bar().baz.buzz && foo.bar().baz.buzz()
Loading
Loading