diff --git a/crates/rome_js_analyze/src/analyzers/js.rs b/crates/rome_js_analyze/src/analyzers/js.rs index 0dbfccea325..e5e288aa09a 100644 --- a/crates/rome_js_analyze/src/analyzers/js.rs +++ b/crates/rome_js_analyze/src/analyzers/js.rs @@ -16,10 +16,11 @@ mod no_unnecessary_continue; mod no_unsafe_negation; mod no_unused_template_literal; mod use_block_statements; +mod use_optional_chain; mod use_simplified_logic_expression; mod use_single_case_statement; mod use_single_var_declarator; mod use_template; mod use_valid_typeof; mod use_while; -declare_group! { pub (crate) Js { name : "js" , rules : [self :: no_async_promise_executor :: NoAsyncPromiseExecutor , self :: no_compare_neg_zero :: NoCompareNegZero , self :: no_dead_code :: NoDeadCode , self :: no_debugger :: NoDebugger , self :: no_delete :: NoDelete , self :: no_double_equals :: NoDoubleEquals , self :: no_empty_pattern :: NoEmptyPattern , self :: no_extra_boolean_cast :: NoExtraBooleanCast , self :: no_negation_else :: NoNegationElse , self :: no_shadow_restricted_names :: NoShadowRestrictedNames , self :: no_sparse_array :: NoSparseArray , self :: no_unnecessary_continue :: NoUnnecessaryContinue , self :: no_unsafe_negation :: NoUnsafeNegation , self :: no_unused_template_literal :: NoUnusedTemplateLiteral , self :: use_block_statements :: UseBlockStatements , self :: use_simplified_logic_expression :: UseSimplifiedLogicExpression , self :: use_single_case_statement :: UseSingleCaseStatement , self :: use_single_var_declarator :: UseSingleVarDeclarator , self :: use_template :: UseTemplate , self :: use_valid_typeof :: UseValidTypeof , self :: use_while :: UseWhile ,] } } +declare_group! { pub (crate) Js { name : "js" , rules : [self :: no_async_promise_executor :: NoAsyncPromiseExecutor , self :: no_compare_neg_zero :: NoCompareNegZero , self :: no_dead_code :: NoDeadCode , self :: no_debugger :: NoDebugger , self :: no_delete :: NoDelete , self :: no_double_equals :: NoDoubleEquals , self :: no_empty_pattern :: NoEmptyPattern , self :: no_extra_boolean_cast :: NoExtraBooleanCast , self :: no_negation_else :: NoNegationElse , self :: no_shadow_restricted_names :: NoShadowRestrictedNames , self :: no_sparse_array :: NoSparseArray , self :: no_unnecessary_continue :: NoUnnecessaryContinue , self :: no_unsafe_negation :: NoUnsafeNegation , self :: no_unused_template_literal :: NoUnusedTemplateLiteral , self :: use_block_statements :: UseBlockStatements , self :: use_optional_chain :: UseOptionalChain , self :: use_simplified_logic_expression :: UseSimplifiedLogicExpression , self :: use_single_case_statement :: UseSingleCaseStatement , self :: use_single_var_declarator :: UseSingleVarDeclarator , self :: use_template :: UseTemplate , self :: use_valid_typeof :: UseValidTypeof , self :: use_while :: UseWhile ,] } } diff --git a/crates/rome_js_analyze/src/analyzers/js/use_optional_chain.rs b/crates/rome_js_analyze/src/analyzers/js/use_optional_chain.rs new file mode 100644 index 00000000000..f16e093d9b5 --- /dev/null +++ b/crates/rome_js_analyze/src/analyzers/js/use_optional_chain.rs @@ -0,0 +1,862 @@ +use std::cmp::Ordering; +use std::collections::VecDeque; +use std::iter; + +use rome_analyze::{ + context::RuleContext, declare_rule, ActionCategory, Ast, Rule, RuleCategory, RuleDiagnostic, +}; +use rome_console::markup; +use rome_diagnostics::Applicability; +use rome_js_factory::make; +use rome_js_syntax::{ + JsAnyExpression, JsAnyName, JsComputedMemberExpression, JsLogicalExpression, JsLogicalOperator, + JsStaticMemberExpression, OperatorPrecedence, T, +}; +use rome_rowan::{declare_node_union, AstNode, AstNodeExt, BatchMutationExt, SyntaxResult}; + +use crate::JsRuleAction; + +declare_rule! { + /// Enforce using concise optional chain instead of chained logical expressions. + /// + /// TypeScript 3.7 added support for the optional chain operator. + /// This operator allows you to safely access properties and methods on objects when they are potentially `null` or `undefined`. + /// The optional chain operator only chains when the property value is `null` or `undefined`. + /// It is much safer than relying upon logical operator chaining; which chains on any truthy value. + /// + /// ## Examples + /// + /// ### Invalid + /// + /// ```js,expect_diagnostic + /// foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz + /// ``` + /// + /// ```js,expect_diagnostic + /// foo.bar && foo.bar.baz.buzz + /// ``` + /// + /// ```js,expect_diagnostic + /// foo !== undefined && foo.bar != undefined && foo.bar.baz !== null && foo.bar.baz.buzz + /// ``` + /// + /// ```js,expect_diagnostic + /// ((foo || {}).bar || {}).baz; + /// ``` + /// + /// ```js,expect_diagnostic + /// (await (foo1 || {}).foo2 || {}).foo3; + /// ``` + /// + /// ```ts,expect_diagnostic + /// (((typeof x) as string) || {}).bar; + /// ``` + /// + /// ### Valid + /// + /// ```js + /// foo && bar; + ///``` + /// ```js + /// foo || {}; + ///``` + /// + /// ```js + /// (foo = 2 || {}).bar; + ///``` + /// + /// ```js + /// foo || foo.bar; + ///``` + /// + /// ```js + /// foo["some long"] && foo["some long string"].baz + ///``` + /// + pub(crate) UseOptionalChain { + version: "0.10.0", + name: "useOptionalChain", + recommended: true, + } +} + +pub(crate) enum UseOptionalChainState { + LogicalAnd(VecDeque), + LogicalOrLike(LogicalOrLikeChain), +} + +impl Rule for UseOptionalChain { + const CATEGORY: RuleCategory = RuleCategory::Lint; + + type Query = Ast; + type State = UseOptionalChainState; + type Signals = Option; + + fn run(ctx: &RuleContext) -> Option { + let logical = ctx.query(); + let operator = logical.operator().ok()?; + + match operator { + JsLogicalOperator::LogicalAnd => { + let head = logical.right().ok()?; + + let chain = LogicalAndChain::from_expression(head).ok()?; + + if chain.is_inside_another_chain().ok()? { + return None; + } + + let optional_chain_expression_nodes = chain.optional_chain_expression_nodes()?; + + Some(UseOptionalChainState::LogicalAnd( + optional_chain_expression_nodes, + )) + } + JsLogicalOperator::NullishCoalescing | JsLogicalOperator::LogicalOr => { + let chain = LogicalOrLikeChain::from_expression(logical)?; + + if chain.is_inside_another_chain() { + return None; + } + + Some(UseOptionalChainState::LogicalOrLike(chain)) + } + } + } + + fn diagnostic(ctx: &RuleContext, state: &Self::State) -> Option { + let range = match state { + UseOptionalChainState::LogicalAnd(_) => ctx.query().range(), + UseOptionalChainState::LogicalOrLike(state) => state.member.range(), + }; + + Some(RuleDiagnostic::new( + range, + markup! { + "Change to an optional chain." + } + .to_owned(), + )) + } + + fn action(ctx: &RuleContext, state: &Self::State) -> Option { + match state { + UseOptionalChainState::LogicalAnd(optional_chain_expression_nodes) => { + let mut prev_expression = None; + + for expression in optional_chain_expression_nodes { + let next_expression = prev_expression + .take() + .and_then(|(prev_expression, new_expression)| { + expression + .clone() + .replace_node(prev_expression, new_expression) + }) + .unwrap_or_else(|| expression.clone()); + + let next_expression = match next_expression { + JsAnyExpression::JsCallExpression(call_expression) => { + let mut call_expression_builder = make::js_call_expression( + call_expression.callee().ok()?, + call_expression.arguments().ok()?, + ) + .with_optional_chain_token(make::token(T![?.])); + + if let Some(type_arguments) = call_expression.type_arguments() { + call_expression_builder = + call_expression_builder.with_type_arguments(type_arguments); + } + + let call_expression = call_expression_builder.build(); + + JsAnyExpression::from(call_expression) + } + JsAnyExpression::JsStaticMemberExpression(member_expression) => { + let operator_token = member_expression.operator_token().ok()?; + + JsAnyExpression::from(make::js_static_member_expression( + member_expression.object().ok()?, + make::token(T![?.]) + .with_leading_trivia_pieces( + operator_token.leading_trivia().pieces(), + ) + .with_trailing_trivia_pieces( + operator_token.trailing_trivia().pieces(), + ), + member_expression.member().ok()?, + )) + } + JsAnyExpression::JsComputedMemberExpression(member_expression) => { + let operator_token = match member_expression.optional_chain_token() { + Some(token) => make::token(T![?.]) + .with_leading_trivia_pieces(token.leading_trivia().pieces()) + .with_trailing_trivia_pieces(token.trailing_trivia().pieces()), + None => make::token(T![?.]), + }; + + JsAnyExpression::from( + make::js_computed_member_expression( + member_expression.object().ok()?, + member_expression.l_brack_token().ok()?, + member_expression.member().ok()?, + member_expression.r_brack_token().ok()?, + ) + .with_optional_chain_token(operator_token) + .build(), + ) + } + _ => return None, + }; + + prev_expression = Some((expression.clone(), next_expression)); + } + + let (prev_expression, new_expression) = prev_expression?; + + let logical = ctx.query(); + let next_right = logical + .right() + .ok()? + .replace_node(prev_expression, new_expression.clone()) + .unwrap_or(new_expression); + + let mut mutation = ctx.root().begin(); + + mutation.replace_node(JsAnyExpression::from(logical.clone()), next_right); + + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Change to an optional chain." }.to_owned(), + mutation, + }) + } + UseOptionalChainState::LogicalOrLike(chain) => { + let chain = chain.optional_chain_expression_nodes(); + + let mut prev_chain: Option<(JsAnyMemberExpression, JsAnyMemberExpression)> = None; + + for (left, member) in chain { + let left = if let Some((prev_member, next_member)) = prev_chain.take() { + left.replace_node(prev_member, next_member.clone()) + .unwrap_or_else(|| next_member.into()) + } else { + left + }; + + let left = trim_trailing_space(left)?; + let need_parenthesis = + left.precedence().ok()? < OperatorPrecedence::LeftHandSide; + + let left = if need_parenthesis { + make::js_parenthesized_expression( + make::token(T!['(']), + left, + make::token(T![')']), + ) + .into() + } else { + left + }; + + let next_member = match member.clone() { + JsAnyMemberExpression::JsStaticMemberExpression(expression) => { + let static_member_expression = make::js_static_member_expression( + left, + make::token(T![?.]), + expression.member().ok()?, + ); + JsAnyMemberExpression::from(static_member_expression) + } + JsAnyMemberExpression::JsComputedMemberExpression(expression) => { + let computed_member_expression = make::js_computed_member_expression( + left, + expression.l_brack_token().ok()?, + expression.member().ok()?, + expression.r_brack_token().ok()?, + ) + .with_optional_chain_token(make::token(T![?.])) + .build(); + + computed_member_expression.into() + } + }; + + prev_chain = Some((member, next_member)); + } + + let (prev_member, new_member) = prev_chain?; + + let mut mutation = ctx.root().begin(); + + mutation.replace_node(prev_member, new_member); + + Some(JsRuleAction { + category: ActionCategory::QuickFix, + applicability: Applicability::MaybeIncorrect, + message: markup! { "Change to an optional chain." }.to_owned(), + mutation, + }) + } + } + } +} + +/// `LogicalAndChainOrdering` is the result of a comparison between two logical and chain. +enum LogicalAndChainOrdering { + /// An ordering where a chain is a sub-chain of another. + /// ```js + /// (foo && foo.bar) /* is sub-chain of */ (foo && foo.bar && foo.bar.baz) + /// ``` + SubChain, + /// An ordering where a chain is equal to another. + /// ```js + /// (foo && foo.bar) /* is equal */ (foo && foo.bar) + /// ``` + Equal, + /// An ordering where a chain is different to another. + /// ```js + /// (foo && foo.bar) /* is different */ (bar && bar.bar && bar.bar.baz) + /// ``` + Different, +} + +/// `LogicalAndChain` handles cases with `JsLogicalExpression` which has `JsLogicalOperator::LogicalAnd` operator: +/// ```js +/// foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz; +/// +/// foo.bar && foo.bar.baz && foo.bar.baz.buzz; +/// +/// foo !== undefined && foo.bar; +/// ``` +/// The main idea of the `LogicalAndChain`: +/// 1. Check that the current chain isn't in another `LogicalAndChain`. We need to find the topmost logical expression which will be the head of the first current chain. +/// 2. Go down thought logical expressions and collect other chains and compare them with the current one. +/// 3. If a chain is a sub-chain of the current chain, we assign that sub-chain to new current one. Difference between current chain and sub-chain is a tail. +/// 4. Save the first tail `JsAnyExpression` to the buffer. +/// 5. Transform every `JsAnyExpression` from the buffer to optional expression. +/// +/// E.g. `foo && foo.bar.baz && foo.bar.baz.zoo;`. +/// The logical expression `foo && foo.bar.baz` isn't the topmost. We skip it. +/// `foo && foo.bar.baz && foo.bar.baz.zoo;` is the topmost and it'll be a start point. +/// We start collecting a chain. We collect `JsAnyExpression` but for clarity let's use string identifiers. +/// `foo.bar.baz.zoo;` -> `[foo, bar, baz, zoo]` +/// Next step we take a next chain and also collect it. +/// `foo.bar.baz` -> `[foo, bar, baz]` +/// By comparing them we understand that one is a sub-chain of the other. `[foo, bar, baz]` is new current chain. `[zoo]` is a tail. +/// We save `zoo` expression to the buffer. +/// Next step we take a next chain and also collect it. +/// `foo` -> `[foo]` +/// By comparing them we understand that one is a sub-chain of the other. `[foo]` is new current chain. `[bar, baz]` is a tail. +/// We save `bar` expression to the buffer. +/// Iterate buffer `[bar, zoo]` we need to make every `JsAnyExpression` optional: `foo?.bar.baz?.zoo;` +/// +#[derive(Debug)] +pub(crate) struct LogicalAndChain { + head: JsAnyExpression, + /// The buffer of `JsAnyExpression` which need to make optional chain. + buf: VecDeque, +} + +impl LogicalAndChain { + fn from_expression(head: JsAnyExpression) -> SyntaxResult { + /// Iterate over `JsAnyExpression` and collect every expression which is a part of the chain: + /// ```js + /// foo.bar[baz]; + /// ``` + /// `[JsReferenceIdentifier, JsStaticMemberExpression, JsComputedMemberExpression]` + fn collect_chain(expression: JsAnyExpression) -> SyntaxResult> { + let mut buf = VecDeque::new(); + + let mut current_expression = Some(expression); + + while let Some(expression) = current_expression.take() { + let expression = match expression { + // Extract a left `JsAnyExpression` from `JsBinaryExpression` if it's optional chain like + // ```js + // (foo === undefined) && foo.bar; + // ``` + // is roughly equivalent to + // ```js + // foo && foo.bar; + // ``` + JsAnyExpression::JsBinaryExpression(expression) => { + if expression.is_optional_chain_like()? { + expression.left()? + } else { + return Ok(buf); + } + } + expression => expression, + }; + + current_expression = match &expression { + JsAnyExpression::JsStaticMemberExpression(member_expression) => { + let object = member_expression.object()?; + buf.push_front(expression); + + Some(object) + } + JsAnyExpression::JsComputedMemberExpression(member_expression) => { + let object = member_expression.object()?; + buf.push_front(expression); + + Some(object) + } + JsAnyExpression::JsCallExpression(call_expression) => { + let callee = call_expression.callee()?; + buf.push_front(expression); + + Some(callee) + } + JsAnyExpression::JsIdentifierExpression(_) => { + buf.push_front(expression); + + return Ok(buf); + } + _ => return Ok(buf), + }; + } + + Ok(buf) + } + + let buf = collect_chain(head.clone())?; + + Ok(LogicalAndChain { head, buf }) + } + + /// This function checks if `LogicalAndChain` is inside another parent `LogicalAndChain` + /// and the chain is sub-chain of parent chain. + fn is_inside_another_chain(&self) -> SyntaxResult { + // Because head of the chain is right expression of logical expression we need to take a parent and a grand-parent. + // E.g. `foo && foo.bar && foo.bar.baz` + // The head of the sub-chain is `foo.bar`. + // The parent of the head is logical expression `foo && foo.bar` + // The grand-parent of the head is logical expression `foo && foo.bar && foo.bar.baz` + if let Some(parent) = self.head.parent::() { + if let Some(grand_parent) = parent.parent::() { + let grand_parent_operator = grand_parent.operator()?; + + if !matches!(grand_parent_operator, JsLogicalOperator::LogicalAnd) { + return Ok(false); + } + + let grand_parent_logical_left = grand_parent.left()?; + + // Here we check that we came from the left side of the logical expression. + // Because only the left-hand parts can be sub-chains. + if grand_parent_logical_left.as_js_logical_expression() == Some(&parent) { + let grand_parent_right_chain = + LogicalAndChain::from_expression(grand_parent.right()?)?; + + let result = grand_parent_right_chain.cmp_chain(self)?; + + return match result { + LogicalAndChainOrdering::SubChain | LogicalAndChainOrdering::Equal => { + Ok(true) + } + LogicalAndChainOrdering::Different => Ok(false), + }; + } + } + } + Ok(false) + } + + /// This function compares two `LogicalAndChain` and returns `LogicalAndChainOrdering` + /// by comparing their `token_text_trimmed` for every `JsAnyExpression` node. + fn cmp_chain(&self, other: &LogicalAndChain) -> SyntaxResult { + let chain_ordering = match self.buf.len().cmp(&other.buf.len()) { + Ordering::Less => return Ok(LogicalAndChainOrdering::Different), + Ordering::Equal => LogicalAndChainOrdering::Equal, + Ordering::Greater => LogicalAndChainOrdering::SubChain, + }; + + for (main_expression, branch_expression) in self.buf.iter().zip(&other.buf) { + let (main_expression, branch_expression) = match (&main_expression, &branch_expression) + { + ( + JsAnyExpression::JsCallExpression(main_expression), + JsAnyExpression::JsCallExpression(branch_expression), + ) => (main_expression.callee()?, branch_expression.callee()?), + _ => (main_expression.clone(), branch_expression.clone()), + }; + + let (main_value_token, branch_value_token) = match (main_expression, branch_expression) + { + ( + JsAnyExpression::JsComputedMemberExpression(main_expression), + JsAnyExpression::JsComputedMemberExpression(branch_expression), + ) => match (main_expression.member()?, branch_expression.member()?) { + ( + JsAnyExpression::JsIdentifierExpression(main_identifier), + JsAnyExpression::JsIdentifierExpression(branch_identifier), + ) => ( + main_identifier.name()?.value_token()?, + branch_identifier.name()?.value_token()?, + ), + ( + JsAnyExpression::JsAnyLiteralExpression(main_expression), + JsAnyExpression::JsAnyLiteralExpression(branch_expression), + ) => ( + main_expression.value_token()?, + branch_expression.value_token()?, + ), + _ => return Ok(LogicalAndChainOrdering::Different), + }, + ( + JsAnyExpression::JsStaticMemberExpression(main_expression), + JsAnyExpression::JsStaticMemberExpression(branch_expression), + ) => match (main_expression.member()?, branch_expression.member()?) { + (JsAnyName::JsName(main_name), JsAnyName::JsName(branch_name)) => { + (main_name.value_token()?, branch_name.value_token()?) + } + ( + JsAnyName::JsPrivateName(main_name), + JsAnyName::JsPrivateName(branch_name), + ) => (main_name.value_token()?, branch_name.value_token()?), + _ => return Ok(LogicalAndChainOrdering::Different), + }, + ( + JsAnyExpression::JsIdentifierExpression(main_expression), + JsAnyExpression::JsIdentifierExpression(branch_expression), + ) => ( + main_expression.name()?.value_token()?, + branch_expression.name()?.value_token()?, + ), + _ => return Ok(LogicalAndChainOrdering::Different), + }; + + if main_value_token.token_text_trimmed() != branch_value_token.token_text_trimmed() { + return Ok(LogicalAndChainOrdering::Different); + } + } + + Ok(chain_ordering) + } + + /// This function returns a list of `JsAnyExpression` which we need to transform into an option chain expression. + fn optional_chain_expression_nodes(mut self) -> Option> { + let mut optional_chain_expression_nodes = VecDeque::with_capacity(self.buf.len()); + + // Take a head of a next sub-chain + // E.g. `foo && foo.bar && foo.bar.baz` + // The head is `foo.bar.baz` expression. + // The parent of the head is a logical expression `foo && foo.bar && foo.bar.baz`. + // The next chain head is a left part of the logical expression `foo && foo.bar` + let mut next_chain_head = self.head.parent::()?.left().ok(); + + while let Some(expression) = next_chain_head.take() { + let expression = match expression { + // Extract a left `JsAnyExpression` from `JsBinaryExpression` if it's optional chain like + // ```js + // (foo === undefined) && foo.bar; + // ``` + // is roughly equivalent to + // ```js + // foo && foo.bar; + // ``` + JsAnyExpression::JsBinaryExpression(expression) => expression + .is_optional_chain_like() + .ok()? + .then_some(expression.left().ok()?)?, + expression => expression, + }; + + let head = match expression { + JsAnyExpression::JsLogicalExpression(logical) => { + if matches!(logical.operator().ok()?, JsLogicalOperator::LogicalAnd) { + // Here we move our sub-chain head over the chains of logical expression + next_chain_head = logical.left().ok(); + + logical.right().ok()? + } else { + return None; + } + } + JsAnyExpression::JsIdentifierExpression(_) + | JsAnyExpression::JsStaticMemberExpression(_) + | JsAnyExpression::JsComputedMemberExpression(_) + | JsAnyExpression::JsCallExpression(_) => expression, + _ => return None, + }; + + let branch = LogicalAndChain::from_expression(head).ok()?; + + match self.cmp_chain(&branch).ok()? { + LogicalAndChainOrdering::SubChain => { + // Here we reduce our main `JsAnyExpression` buffer by splitting the main buffer. + // Let's say that we have two buffers: + // The main is `[foo, bar, baz]` and a branch is `[foo]` + // After splitting the main buffer will be `[foo]` and the tail will be `[bar, baz]`. + // It means that we need to transform `bar` (first tail expression) into the optional one. + let mut tail = self.buf.split_off(branch.buf.len()); + + if let Some(part) = tail.pop_front() { + optional_chain_expression_nodes.push_front(part) + }; + } + LogicalAndChainOrdering::Equal => continue, + LogicalAndChainOrdering::Different => return None, + } + } + + if optional_chain_expression_nodes.is_empty() { + return None; + } + + Some(optional_chain_expression_nodes) + } +} + +/// `LogicalOrLikeChain` handles cases with `JsLogicalExpression` which has `JsLogicalOperator::NullishCoalescing` or `JsLogicalOperator::LogicalOr` operator: +/// ```js +/// (foo || {}).bar; +/// (foo ?? {}).bar; +/// ((foo ?? {}).bar || {}).baz; +/// ``` +/// The main idea of the `LogicalOrLikeChain`: +/// 1. Check that the current member expressions isn't in another `LogicalOrLikeChain`. We need to find the topmost member expression. +/// 2. Go down thought logical expressions and collect left and member expressions to buffer. +/// 3. Transform every left `JsAnyExpression` and member `JsAnyMemberExpression` expressions into optional `JsAnyMemberExpression`. +/// +/// E.g. `((foo ?? {}).bar || {}).baz;`. +/// The member expression `(foo ?? {}).bar` isn't the topmost. We skip it. +/// `((foo ?? {}).bar || {}).baz;` is the topmost and it'll be a start point. +/// We start collecting pairs of a left and member expressions to buffer. +/// First expression is `((foo ?? {}).bar || {}).baz;`: +/// Buffer is `[((foo ?? {}).bar, ((foo ?? {}).bar || {}).baz;)]` +/// Next expressions is `((foo ?? {}).bar || {}).baz;`: +/// Buffer is `[(foo, (foo ?? {}).bar), ((foo ?? {}).bar, ((foo ?? {}).bar || {}).baz;)]` +/// Iterate buffer, take member expressions and replace object with left parts and make the expression optional chain: +/// `foo?.bar?.baz;` +/// +#[derive(Debug)] +pub(crate) struct LogicalOrLikeChain { + member: JsAnyMemberExpression, +} + +impl LogicalOrLikeChain { + /// Create a `LogicalOrLikeChain` if `JsLogicalExpression` is optional chain like and the `JsLogicalExpression` is inside member expression. + /// ```js + /// (foo || {}).bar; + /// ``` + fn from_expression(logical: &JsLogicalExpression) -> Option { + let is_right_empty_object = logical + .right() + .ok()? + // Handle case when a right expression is inside parentheses + // E.g. (foo || (({}))).bar; + .omit_parentheses() + .as_js_object_expression()? + .is_empty(); + + if !is_right_empty_object { + return None; + } + + let member = + LogicalOrLikeChain::get_chain_parent_member(JsAnyExpression::from(logical.clone()))?; + + Some(LogicalOrLikeChain { member }) + } + + /// This function checks if `LogicalOrLikeChain` is inside another parent `LogicalOrLikeChain`. + /// E.g. + /// `(foo ?? {}).bar` is inside `((foo ?? {}).bar || {}).baz;` + fn is_inside_another_chain(&self) -> bool { + LogicalOrLikeChain::get_chain_parent(JsAnyExpression::from(self.member.clone())).map_or( + false, + |parent| { + parent + .as_js_logical_expression() + .filter(|parent_expression| { + matches!( + parent_expression.operator(), + Ok(JsLogicalOperator::NullishCoalescing | JsLogicalOperator::LogicalOr) + ) + }) + .and_then(LogicalOrLikeChain::from_expression) + .is_some() + }, + ) + } + + /// This function returns a list of pairs `(JsAnyExpression, JsAnyMemberExpression)` which we need to transform into an option chain expression. + fn optional_chain_expression_nodes( + &self, + ) -> VecDeque<(JsAnyExpression, JsAnyMemberExpression)> { + let mut chain = VecDeque::new(); + + // Start from the topmost member expression + let mut next_member_chain = Some(self.member.clone()); + + while let Some(member) = next_member_chain.take() { + let object = match member.object() { + Ok(object) => object, + _ => return chain, + }; + + // Handle case when a object expression is inside parentheses + // E.g. (((foo || {}))).bar; + let object = object.omit_parentheses(); + + if let JsAnyExpression::JsLogicalExpression(logical) = object { + let is_valid_operator = logical.operator().map_or(false, |operator| { + matches!( + operator, + JsLogicalOperator::NullishCoalescing | JsLogicalOperator::LogicalOr + ) + }); + + if !is_valid_operator { + return chain; + } + + let is_right_empty_object = logical + .right() + .ok() + .and_then(|right| { + right + // Handle case when a right expression is inside parentheses + // E.g. (foo || (({}))).bar; + .omit_parentheses() + .as_js_object_expression() + .map(|object| object.is_empty()) + }) + .unwrap_or(false); + + if !is_right_empty_object { + return chain; + } + + let left = match logical.left() { + Ok(left) => left, + Err(_) => return chain, + }; + + // Set next member expression from the left part + // Find next member expression + // E.g. `((foo || {}).baz() || {}).bar` + // If current member chain is `bar` the next member chain is baz. + // Need to downward traversal to find first `JsAnyExpression` which we can't include in chain + next_member_chain = LogicalOrLikeChain::get_member(left.clone()); + + chain.push_front((left, member)) + } + } + + chain + } + + /// Traversal by parent to find the parent member of a chain. + fn get_chain_parent_member(expression: JsAnyExpression) -> Option { + iter::successors(expression.parent::(), |expression| { + if matches!(expression, JsAnyExpression::JsParenthesizedExpression(_)) { + expression.parent::() + } else { + None + } + }) + .last() + .and_then(|parent| { + let member = match parent { + JsAnyExpression::JsComputedMemberExpression(expression) => { + JsAnyMemberExpression::from(expression) + } + JsAnyExpression::JsStaticMemberExpression(expression) => { + JsAnyMemberExpression::from(expression) + } + _ => return None, + }; + Some(member) + }) + } + + /// Traversal by parent to find the parent of a chain. + /// This function is opposite to the `get_member` function. + fn get_chain_parent(expression: JsAnyExpression) -> Option { + iter::successors(expression.parent::(), |expression| { + if matches!( + expression, + JsAnyExpression::JsParenthesizedExpression(_) + | JsAnyExpression::JsAwaitExpression(_) + | JsAnyExpression::JsCallExpression(_) + | JsAnyExpression::JsNewExpression(_) + | JsAnyExpression::TsAsExpression(_) + | JsAnyExpression::TsNonNullAssertionExpression(_) + | JsAnyExpression::TsTypeAssertionExpression(_) + ) { + expression.parent::() + } else { + None + } + }) + .last() + } + + /// Downward traversal to find the member. + /// E.g. `((foo || {}).baz() || {}).bar` + /// If current member chain is `bar` the next member chain is baz. + /// Need to downward traversal to find first `JsAnyExpression` which we can't include in chain. + fn get_member(expression: JsAnyExpression) -> Option { + let expression = iter::successors(Some(expression), |expression| match expression { + JsAnyExpression::JsParenthesizedExpression(expression) => expression.expression().ok(), + JsAnyExpression::JsAwaitExpression(expression) => expression.argument().ok(), + JsAnyExpression::JsCallExpression(expression) => expression.callee().ok(), + JsAnyExpression::JsNewExpression(expression) => expression.callee().ok(), + JsAnyExpression::TsAsExpression(expression) => expression.expression().ok(), + JsAnyExpression::TsNonNullAssertionExpression(expression) => { + expression.expression().ok() + } + JsAnyExpression::TsTypeAssertionExpression(expression) => expression.expression().ok(), + _ => None, + }) + .last()?; + + let expression = match expression { + JsAnyExpression::JsComputedMemberExpression(expression) => { + JsAnyMemberExpression::from(expression) + } + JsAnyExpression::JsStaticMemberExpression(expression) => { + JsAnyMemberExpression::from(expression) + } + _ => return None, + }; + + Some(expression) + } +} + +fn trim_trailing_space(node: JsAnyExpression) -> Option { + if let Some(last_token_of_left_syntax) = node.syntax().last_token() { + let next_token_of_left_syntax = + last_token_of_left_syntax.with_trailing_trivia(std::iter::empty()); + node.replace_token_discard_trivia(last_token_of_left_syntax, next_token_of_left_syntax) + } else { + Some(node) + } +} + +declare_node_union! { + pub (crate) JsAnyMemberExpression = JsComputedMemberExpression | JsStaticMemberExpression +} + +impl From for JsAnyExpression { + fn from(expression: JsAnyMemberExpression) -> Self { + match expression { + JsAnyMemberExpression::JsComputedMemberExpression(expression) => expression.into(), + JsAnyMemberExpression::JsStaticMemberExpression(expression) => expression.into(), + } + } +} + +impl JsAnyMemberExpression { + fn object(&self) -> SyntaxResult { + match self { + JsAnyMemberExpression::JsComputedMemberExpression(expression) => expression.object(), + JsAnyMemberExpression::JsStaticMemberExpression(expression) => expression.object(), + } + } +} diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index 8889afd890c..238e1cd7c13 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -106,6 +106,32 @@ mod tests { use crate::{analyze, AnalysisFilter, ControlFlow}; + #[ignore] + #[test] + fn quick_test() { + const SOURCE: &str = " + foo.bar && foo.bar?.(); + "; + + let parsed = parse(SOURCE, 0, SourceType::js_module()); + + let mut error_ranges = Vec::new(); + analyze(0, &parsed.tree(), AnalysisFilter::default(), |signal| { + if let Some(diag) = signal.diagnostic() { + let diag = diag.into_diagnostic(Severity::Warning); + let primary = diag.primary.as_ref().unwrap(); + + error_ranges.push(primary.span.range); + } + + dbg!(signal.action()); + + ControlFlow::::Continue(()) + }); + + assert_eq!(error_ranges.as_slice(), &[]); + } + #[test] fn suppression() { const SOURCE: &str = " diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts new file mode 100644 index 00000000000..6007acf241d --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts @@ -0,0 +1,4 @@ +// currently do not handle complex computed properties +foo && foo[bar as string] && foo[bar as string].baz; +foo && foo[1 + 2] && foo[1 + 2].baz; +foo && foo[typeof bar] && foo[typeof bar].baz; diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts.snap b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts.snap new file mode 100644 index 00000000000..654506b2975 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/complexLogicalAndCases.ts.snap @@ -0,0 +1,69 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: complexLogicalAndCases.ts +--- +# Input +```js +// currently do not handle complex computed properties +foo && foo[bar as string] && foo[bar as string].baz; +foo && foo[1 + 2] && foo[1 + 2].baz; +foo && foo[typeof bar] && foo[typeof bar].baz; + +``` + +# Diagnostics +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ complexLogicalAndCases.ts:2:1 + │ +2 │ foo && foo[bar as string] && foo[bar as string].baz; + │ ------------------------- + +Suggested fix: Change to an optional chain. + | @@ -1,4 +1,4 @@ +0 0 | // currently do not handle complex computed properties +1 | - foo && foo[bar as string] && foo[bar as string].baz; + 1 | + foo?.[bar as string] && foo[bar as string].baz; +2 2 | foo && foo[1 + 2] && foo[1 + 2].baz; +3 3 | foo && foo[typeof bar] && foo[typeof bar].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ complexLogicalAndCases.ts:3:1 + │ +3 │ foo && foo[1 + 2] && foo[1 + 2].baz; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -1,4 +1,4 @@ +0 0 | // currently do not handle complex computed properties +1 1 | foo && foo[bar as string] && foo[bar as string].baz; +2 | - foo && foo[1 + 2] && foo[1 + 2].baz; + 2 | + foo?.[1 + 2] && foo[1 + 2].baz; +3 3 | foo && foo[typeof bar] && foo[typeof bar].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ complexLogicalAndCases.ts:4:1 + │ +4 │ foo && foo[typeof bar] && foo[typeof bar].baz; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -1,4 +1,4 @@ +0 0 | // currently do not handle complex computed properties +1 1 | foo && foo[bar as string] && foo[bar as string].baz; +2 2 | foo && foo[1 + 2] && foo[1 + 2].baz; +3 | - foo && foo[typeof bar] && foo[typeof bar].baz; + 3 | + foo?.[typeof bar] && foo[typeof bar].baz; + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js new file mode 100644 index 00000000000..54f7e7eee80 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js @@ -0,0 +1,384 @@ +// base cases + +// chained members +foo && foo.bar +foo.bar && foo.bar.baz +foo && foo() +foo.bar && foo.bar() +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +foo.bar && foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz +foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz + +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() +foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() + +// 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() + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +foo && foo?.bar.baz && foo?.bar.baz[buzz] +foo && foo?.() && foo?.().bar +foo.bar && foo.bar?.() && foo.bar?.().baz + + +// it should ignore parts of the expression that aren't part of the expression chain + +// chained members +foo && foo.bar && bing +foo.bar && foo.bar.baz && bing +foo && foo() && bing +foo.bar && foo.bar() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz.buzz && bing + +// case where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz && bing + +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() && bing +foo.bar && foo.bar.baz.buzz() && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + +// 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 + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing +foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing +foo && foo?.() && foo?.().bar && bing +foo.bar && foo.bar?.() && foo.bar?.().baz && bing + +// chained members +foo && foo.bar && bing.bong +foo.bar && foo.bar.baz && bing.bong +foo && foo() && bing.bong +foo.bar && foo.bar() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +foo.bar && foo.bar.baz && 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 where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong + +// 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 x !== null && x.y !== null +// chained members +foo !== null && foo.bar +foo.bar !== null && foo.bar.baz +foo !== null && foo() +foo.bar !== null && foo.bar() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + +// chained members with element access +foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz + +// chained calls +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz() +foo.bar !== null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() + +// chained calls with element access +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() +foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] +foo !== null && foo?.() !== null && foo?.().bar +foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz + +// chained members +foo !== undefined && foo.bar +foo.bar !== undefined && foo.bar.baz +foo !== undefined && foo() +foo.bar !== undefined && foo.bar() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + +// chained members with element access +foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + +// chained calls +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() +foo.bar !== undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() + +// chained calls with element access +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() +foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] +foo !== undefined && foo?.() !== undefined && foo?.().bar +foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz + +// chained members +foo != null && foo.bar +foo.bar != null && foo.bar.baz +foo != null && foo() +foo.bar != null && foo.bar() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + +// chained members with element access +foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo[bar].baz != null && foo[bar].baz.buzz + +// chained calls +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz() +foo.bar != null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() + +// chained calls with element access +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() +foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] +foo != null && foo?.() != null && foo?.().bar +foo.bar != null && foo.bar?.() != null && foo.bar?.().baz + +// chained members +foo != undefined && foo.bar +foo.bar != undefined && foo.bar.baz +foo != undefined && foo() +foo.bar != undefined && foo.bar() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + +// chained members with element access +foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + +// chained calls +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() +foo.bar != undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() + +// chained calls with element access +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() +foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] +foo != undefined && foo?.() != undefined && foo?.().bar +foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz + +//private static member name +foo && foo.#bar +foo.#bar && foo.#bar.#baz +foo.#bar && foo.#bar() +foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + +// two errors +foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + +// case with inconsistent checks +foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + +foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; + +// ensure essential whitespace isn't removed +foo && foo.bar(baz => ); +foo && foo.bar(baz => typeof baz); +foo && foo["some long string"] && foo["some long string"].baz +foo && foo[`some long string`] && foo[`some long string`].baz +foo && foo['some long string'] && foo['some long string'].baz; + +// other literal expressions +foo && foo[123] && foo[123].baz; +foo && foo[true] && foo[true].baz; +foo && foo[null] && foo[null].baz; +foo && foo[12n] && foo[12n].baz; +foo && foo[/\w+/] && foo[/\w+/].baz; + + +// should preserve comments in a call expression +foo && foo.bar(/* comment */a, + // comment2 + b, ); + +// ensure binary expressions that are the last expression do not get removed +foo && foo.bar != null; +foo && foo.bar != undefined; +foo && foo.bar != null && baz; + +// other weird cases +foo && foo?.(); +foo.bar && foo.bar?.(); + +// comments +foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + +foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js.snap b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js.snap new file mode 100644 index 00000000000..21ea7e00e21 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/logicalAndCases.js.snap @@ -0,0 +1,4656 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: logicalAndCases.js +--- +# Input +```js +// base cases + +// chained members +foo && foo.bar +foo.bar && foo.bar.baz +foo && foo() +foo.bar && foo.bar() +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +foo.bar && foo.bar.baz && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz +foo.bar && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz + +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() +foo.bar && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() + +// 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() + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +foo && foo?.bar.baz && foo?.bar.baz[buzz] +foo && foo?.() && foo?.().bar +foo.bar && foo.bar?.() && foo.bar?.().baz + + +// it should ignore parts of the expression that aren't part of the expression chain + +// chained members +foo && foo.bar && bing +foo.bar && foo.bar.baz && bing +foo && foo() && bing +foo.bar && foo.bar() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz.buzz && bing + +// case where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz && bing + +// chained calls +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz() && bing +foo.bar && foo.bar.baz.buzz() && bing + +// case with a jump (i.e. a non-nullish prop) +foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + +// 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 + +// chained calls with element access +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing +foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing + +// (partially) pre-optional chained +foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing +foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing +foo && foo?.() && foo?.().bar && bing +foo.bar && foo.bar?.() && foo.bar?.().baz && bing + +// chained members +foo && foo.bar && bing.bong +foo.bar && foo.bar.baz && bing.bong +foo && foo() && bing.bong +foo.bar && foo.bar() && bing.bong +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +foo.bar && foo.bar.baz && 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 where for some reason there is a doubled up expression +foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong +foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + +// chained members with element access +foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong + +// case with a jump (i.e. a non-nullish prop) +foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong + +// 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 x !== null && x.y !== null +// chained members +foo !== null && foo.bar +foo.bar !== null && foo.bar.baz +foo !== null && foo() +foo.bar !== null && foo.bar() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + +// chained members with element access +foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz + +// chained calls +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz() +foo.bar !== null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() + +// chained calls with element access +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() +foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() +foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] +foo !== null && foo?.() !== null && foo?.().bar +foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz + +// chained members +foo !== undefined && foo.bar +foo.bar !== undefined && foo.bar.baz +foo !== undefined && foo() +foo.bar !== undefined && foo.bar() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + +// chained members with element access +foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + +// chained calls +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() +foo.bar !== undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() + +// chained calls with element access +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() +foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() +foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] +foo !== undefined && foo?.() !== undefined && foo?.().bar +foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz + +// chained members +foo != null && foo.bar +foo.bar != null && foo.bar.baz +foo != null && foo() +foo.bar != null && foo.bar() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz +foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + +// chained members with element access +foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo[bar].baz != null && foo[bar].baz.buzz + +// chained calls +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz() +foo.bar != null && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() + +// chained calls with element access +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() +foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() +foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] +foo != null && foo?.() != null && foo?.().bar +foo.bar != null && foo.bar?.() != null && foo.bar?.().baz + +// chained members +foo != undefined && foo.bar +foo.bar != undefined && foo.bar.baz +foo != undefined && foo() +foo.bar != undefined && foo.bar() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz.buzz + +// case where for some reason there is a doubled up expression +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + +// chained members with element access +foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + +// chained calls +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() +foo.bar != undefined && foo.bar.baz.buzz() + +// case with a jump (i.e. a non-nullish prop) +foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + +// case with a call expr inside the chain for some inefficient reason +foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() + +// chained calls with element access +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() +foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() + +// (partially) pre-optional chained +foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() +foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] +foo != undefined && foo?.() != undefined && foo?.().bar +foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz + +//private static member name +foo && foo.#bar +foo.#bar && foo.#bar.#baz +foo.#bar && foo.#bar() +foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + +// two errors +foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + +// case with inconsistent checks +foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + +foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; + +// ensure essential whitespace isn't removed +foo && foo.bar(baz => ); +foo && foo.bar(baz => typeof baz); +foo && foo["some long string"] && foo["some long string"].baz +foo && foo[`some long string`] && foo[`some long string`].baz +foo && foo['some long string'] && foo['some long string'].baz; + +// other literal expressions +foo && foo[123] && foo[123].baz; +foo && foo[true] && foo[true].baz; +foo && foo[null] && foo[null].baz; +foo && foo[12n] && foo[12n].baz; +foo && foo[/\w+/] && foo[/\w+/].baz; + + +// should preserve comments in a call expression +foo && foo.bar(/* comment */a, + // comment2 + b, ); + +// ensure binary expressions that are the last expression do not get removed +foo && foo.bar != null; +foo && foo.bar != undefined; +foo && foo.bar != null && baz; + +// other weird cases +foo && foo?.(); +foo.bar && foo.bar?.(); + +// comments +foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + +foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + +``` + +# Diagnostics +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:4:1 + │ +4 │ foo && foo.bar + │ -------------- + +Suggested fix: Change to an optional chain. + | @@ -1,7 +1,7 @@ +0 0 | // base cases +1 1 | +2 2 | // chained members +3 | - foo && foo.bar + 3 | + foo?.bar +4 4 | foo.bar && foo.bar.baz +5 5 | foo && foo() +6 6 | foo.bar && foo.bar() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:5:1 + │ +5 │ foo.bar && foo.bar.baz + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -2,7 +2,7 @@ +1 1 | +2 2 | // chained members +3 3 | foo && foo.bar +4 | - foo.bar && foo.bar.baz + 4 | + foo.bar?.baz +5 5 | foo && foo() +6 6 | foo.bar && foo.bar() +7 7 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:6:1 + │ +6 │ foo && foo() + │ ------------ + +Suggested fix: Change to an optional chain. + | @@ -3,7 +3,7 @@ +2 2 | // chained members +3 3 | foo && foo.bar +4 4 | foo.bar && foo.bar.baz +5 | - foo && foo() + 5 | + foo?.() +6 6 | foo.bar && foo.bar() +7 7 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +8 8 | foo.bar && foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:7:1 + │ +7 │ foo.bar && foo.bar() + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -4,7 +4,7 @@ +3 3 | foo && foo.bar +4 4 | foo.bar && foo.bar.baz +5 5 | foo && foo() +6 | - foo.bar && foo.bar() + 6 | + foo.bar?.() +7 7 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +8 8 | foo.bar && foo.bar.baz && foo.bar.baz.buzz +9 9 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:8:1 + │ +8 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz + │ ------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -5,7 +5,7 @@ +4 4 | foo.bar && foo.bar.baz +5 5 | foo && foo() +6 6 | foo.bar && foo.bar() +7 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz + 7 | + foo?.bar?.baz?.buzz +8 8 | foo.bar && foo.bar.baz && foo.bar.baz.buzz +9 9 | +10 10 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:9:1 + │ +9 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz + │ ------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -6,7 +6,7 @@ +5 5 | foo && foo() +6 6 | foo.bar && foo.bar() +7 7 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +8 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz + 8 | + foo.bar?.baz?.buzz +9 9 | +10 10 | // case with a jump (i.e. a non-nullish prop) +11 11 | foo && foo.bar && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:12:1 + │ +12 │ foo && foo.bar && foo.bar.baz.buzz + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -9,7 +9,7 @@ + 8 8 | foo.bar && foo.bar.baz && foo.bar.baz.buzz + 9 9 | +10 10 | // case with a jump (i.e. a non-nullish prop) +11 | - foo && foo.bar && foo.bar.baz.buzz + 11 | + foo?.bar?.baz.buzz +12 12 | foo.bar && foo.bar.baz.buzz +13 13 | +14 14 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:13:1 + │ +13 │ foo.bar && foo.bar.baz.buzz + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -10,7 +10,7 @@ + 9 9 | +10 10 | // case with a jump (i.e. a non-nullish prop) +11 11 | foo && foo.bar && foo.bar.baz.buzz +12 | - foo.bar && foo.bar.baz.buzz + 12 | + foo.bar?.baz.buzz +13 13 | +14 14 | // case where for some reason there is a doubled up expression +15 15 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:16:1 + │ +16 │ foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -13,7 +13,7 @@ +12 12 | foo.bar && foo.bar.baz.buzz +13 13 | +14 14 | // case where for some reason there is a doubled up expression +15 | - foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + 15 | + foo?.bar?.baz?.buzz +16 16 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz +17 17 | +18 18 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:17:1 + │ +17 │ foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -14,7 +14,7 @@ +13 13 | +14 14 | // case where for some reason there is a doubled up expression +15 15 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz +16 | - foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz + 16 | + foo.bar?.baz?.buzz +17 17 | +18 18 | // chained members with element access +19 19 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:20:1 + │ +20 │ foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -17,7 +17,7 @@ +16 16 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz +17 17 | +18 18 | // chained members with element access +19 | - foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz + 19 | + foo?.[bar]?.baz?.buzz +20 20 | +21 21 | // case with a jump (i.e. a non-nullish prop) +22 22 | foo && foo[bar].baz && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:23:1 + │ +23 │ foo && foo[bar].baz && foo[bar].baz.buzz + │ ---------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -20,7 +20,7 @@ +19 19 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz +20 20 | +21 21 | // case with a jump (i.e. a non-nullish prop) +22 | - foo && foo[bar].baz && foo[bar].baz.buzz + 22 | + foo?.[bar].baz?.buzz +23 23 | +24 24 | // chained calls +25 25 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:26:1 + │ +26 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() + │ --------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -23,7 +23,7 @@ +22 22 | foo && foo[bar].baz && foo[bar].baz.buzz +23 23 | +24 24 | // chained calls +25 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() + 25 | + foo?.bar?.baz?.buzz() +26 26 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +27 27 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +28 28 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:27:1 + │ +27 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ----------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -24,7 +24,7 @@ +23 23 | +24 24 | // chained calls +25 25 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() +26 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + 26 | + foo?.bar?.baz?.buzz?.() +27 27 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +28 28 | +29 29 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:28:1 + │ +28 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -25,7 +25,7 @@ +24 24 | // chained calls +25 25 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() +26 26 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +27 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() + 27 | + foo.bar?.baz?.buzz?.() +28 28 | +29 29 | // case with a jump (i.e. a non-nullish prop) +30 30 | foo && foo.bar && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:31:1 + │ +31 │ foo && foo.bar && foo.bar.baz.buzz() + │ ------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -28,7 +28,7 @@ +27 27 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() +28 28 | +29 29 | // case with a jump (i.e. a non-nullish prop) +30 | - foo && foo.bar && foo.bar.baz.buzz() + 30 | + foo?.bar?.baz.buzz() +31 31 | foo.bar && foo.bar.baz.buzz() +32 32 | +33 33 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:32:1 + │ +32 │ foo.bar && foo.bar.baz.buzz() + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -29,7 +29,7 @@ +28 28 | +29 29 | // case with a jump (i.e. a non-nullish prop) +30 30 | foo && foo.bar && foo.bar.baz.buzz() +31 | - foo.bar && foo.bar.baz.buzz() + 31 | + foo.bar?.baz.buzz() +32 32 | +33 33 | // case with a jump (i.e. a non-nullish prop) +34 34 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:35:1 + │ +35 │ foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() + │ -------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -32,7 +32,7 @@ +31 31 | foo.bar && foo.bar.baz.buzz() +32 32 | +33 33 | // case with a jump (i.e. a non-nullish prop) +34 | - foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() + 34 | + foo?.bar?.baz.buzz?.() +35 35 | +36 36 | // case with a call expr inside the chain for some inefficient reason +37 37 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:38:1 + │ +38 │ foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() + │ ------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -35,7 +35,7 @@ +34 34 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() +35 35 | +36 36 | // case with a call expr inside the chain for some inefficient reason +37 | - foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() + 37 | + foo?.bar()?.baz?.buzz?.() +38 38 | +39 39 | // chained calls with element access +40 40 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:41:1 + │ +41 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -38,7 +38,7 @@ +37 37 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() +38 38 | +39 39 | // chained calls with element access +40 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() + 40 | + foo?.bar?.baz?.[buzz]() +41 41 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() +42 42 | +43 43 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:42:1 + │ +42 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() + │ ------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -39,7 +39,7 @@ +38 38 | +39 39 | // chained calls with element access +40 40 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() +41 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() + 41 | + foo?.bar?.baz?.[buzz]?.() +42 42 | +43 43 | // (partially) pre-optional chained +44 44 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:45:1 + │ +45 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + │ ----------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -42,7 +42,7 @@ +41 41 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() +42 42 | +43 43 | // (partially) pre-optional chained +44 | - foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() + 44 | + foo?.bar?.baz?.[buzz]?.() +45 45 | foo && foo?.bar.baz && foo?.bar.baz[buzz] +46 46 | foo && foo?.() && foo?.().bar +47 47 | foo.bar && foo.bar?.() && foo.bar?.().baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:46:1 + │ +46 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -43,7 +43,7 @@ +42 42 | +43 43 | // (partially) pre-optional chained +44 44 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +45 | - foo && foo?.bar.baz && foo?.bar.baz[buzz] + 45 | + foo?.bar.baz?.[buzz] +46 46 | foo && foo?.() && foo?.().bar +47 47 | foo.bar && foo.bar?.() && foo.bar?.().baz +48 48 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:47:1 + │ +47 │ foo && foo?.() && foo?.().bar + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -44,7 +44,7 @@ +43 43 | // (partially) pre-optional chained +44 44 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +45 45 | foo && foo?.bar.baz && foo?.bar.baz[buzz] +46 | - foo && foo?.() && foo?.().bar + 46 | + foo?.()?.bar +47 47 | foo.bar && foo.bar?.() && foo.bar?.().baz +48 48 | +49 49 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:48:1 + │ +48 │ foo.bar && foo.bar?.() && foo.bar?.().baz + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -45,7 +45,7 @@ +44 44 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() +45 45 | foo && foo?.bar.baz && foo?.bar.baz[buzz] +46 46 | foo && foo?.() && foo?.().bar +47 | - foo.bar && foo.bar?.() && foo.bar?.().baz + 47 | + foo.bar?.()?.baz +48 48 | +49 49 | +50 50 | // it should ignore parts of the expression that aren't part of the expression chain + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:54:1 + │ +54 │ foo && foo.bar && bing + │ -------------- + +Suggested fix: Change to an optional chain. + | @@ -51,7 +51,7 @@ +50 50 | // it should ignore parts of the expression that aren't part of the expression chain +51 51 | +52 52 | // chained members +53 | - foo && foo.bar && bing + 53 | + foo?.bar && bing +54 54 | foo.bar && foo.bar.baz && bing +55 55 | foo && foo() && bing +56 56 | foo.bar && foo.bar() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:55:1 + │ +55 │ foo.bar && foo.bar.baz && bing + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -52,7 +52,7 @@ +51 51 | +52 52 | // chained members +53 53 | foo && foo.bar && bing +54 | - foo.bar && foo.bar.baz && bing + 54 | + foo.bar?.baz && bing +55 55 | foo && foo() && bing +56 56 | foo.bar && foo.bar() && bing +57 57 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:56:1 + │ +56 │ foo && foo() && bing + │ ------------ + +Suggested fix: Change to an optional chain. + | @@ -53,7 +53,7 @@ +52 52 | // chained members +53 53 | foo && foo.bar && bing +54 54 | foo.bar && foo.bar.baz && bing +55 | - foo && foo() && bing + 55 | + foo?.() && bing +56 56 | foo.bar && foo.bar() && bing +57 57 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +58 58 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:57:1 + │ +57 │ foo.bar && foo.bar() && bing + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -54,7 +54,7 @@ +53 53 | foo && foo.bar && bing +54 54 | foo.bar && foo.bar.baz && bing +55 55 | foo && foo() && bing +56 | - foo.bar && foo.bar() && bing + 56 | + foo.bar?.() && bing +57 57 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +58 58 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +59 59 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:58:1 + │ +58 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + │ ------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -55,7 +55,7 @@ +54 54 | foo.bar && foo.bar.baz && bing +55 55 | foo && foo() && bing +56 56 | foo.bar && foo.bar() && bing +57 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + 57 | + foo?.bar?.baz?.buzz && bing +58 58 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +59 59 | +60 60 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:59:1 + │ +59 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + │ ------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -56,7 +56,7 @@ +55 55 | foo && foo() && bing +56 56 | foo.bar && foo.bar() && bing +57 57 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +58 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing + 58 | + foo.bar?.baz?.buzz && bing +59 59 | +60 60 | // case with a jump (i.e. a non-nullish prop) +61 61 | foo && foo.bar && foo.bar.baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:62:1 + │ +62 │ foo && foo.bar && foo.bar.baz.buzz && bing + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -59,7 +59,7 @@ +58 58 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing +59 59 | +60 60 | // case with a jump (i.e. a non-nullish prop) +61 | - foo && foo.bar && foo.bar.baz.buzz && bing + 61 | + foo?.bar?.baz.buzz && bing +62 62 | foo.bar && foo.bar.baz.buzz && bing +63 63 | +64 64 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:63:1 + │ +63 │ foo.bar && foo.bar.baz.buzz && bing + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -60,7 +60,7 @@ +59 59 | +60 60 | // case with a jump (i.e. a non-nullish prop) +61 61 | foo && foo.bar && foo.bar.baz.buzz && bing +62 | - foo.bar && foo.bar.baz.buzz && bing + 62 | + foo.bar?.baz.buzz && bing +63 63 | +64 64 | // case where for some reason there is a doubled up expression +65 65 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:66:1 + │ +66 │ foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -63,7 +63,7 @@ +62 62 | foo.bar && foo.bar.baz.buzz && bing +63 63 | +64 64 | // case where for some reason there is a doubled up expression +65 | - foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + 65 | + foo?.bar?.baz?.buzz && bing +66 66 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing +67 67 | +68 68 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:67:1 + │ +67 │ foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -64,7 +64,7 @@ +63 63 | +64 64 | // case where for some reason there is a doubled up expression +65 65 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing +66 | - foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing + 66 | + foo.bar?.baz?.buzz && bing +67 67 | +68 68 | // chained members with element access +69 69 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:70:1 + │ +70 │ foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -67,7 +67,7 @@ +66 66 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing +67 67 | +68 68 | // chained members with element access +69 | - foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing + 69 | + foo?.[bar]?.baz?.buzz && bing +70 70 | +71 71 | // case with a jump (i.e. a non-nullish prop) +72 72 | foo && foo[bar].baz && foo[bar].baz.buzz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:73:1 + │ +73 │ foo && foo[bar].baz && foo[bar].baz.buzz && bing + │ ---------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -70,7 +70,7 @@ +69 69 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing +70 70 | +71 71 | // case with a jump (i.e. a non-nullish prop) +72 | - foo && foo[bar].baz && foo[bar].baz.buzz && bing + 72 | + foo?.[bar].baz?.buzz && bing +73 73 | +74 74 | // chained calls +75 75 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:76:1 + │ +76 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing + │ --------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -73,7 +73,7 @@ +72 72 | foo && foo[bar].baz && foo[bar].baz.buzz && bing +73 73 | +74 74 | // chained calls +75 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing + 75 | + foo?.bar?.baz?.buzz() && bing +76 76 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +77 77 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +78 78 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:77:1 + │ +77 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + │ ----------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -74,7 +74,7 @@ +73 73 | +74 74 | // chained calls +75 75 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing +76 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + 76 | + foo?.bar?.baz?.buzz?.() && bing +77 77 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +78 78 | +79 79 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:78:1 + │ +78 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -75,7 +75,7 @@ +74 74 | // chained calls +75 75 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing +76 76 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +77 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + 77 | + foo.bar?.baz?.buzz?.() && bing +78 78 | +79 79 | // case with a jump (i.e. a non-nullish prop) +80 80 | foo && foo.bar && foo.bar.baz.buzz() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:81:1 + │ +81 │ foo && foo.bar && foo.bar.baz.buzz() && bing + │ ------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -78,7 +78,7 @@ +77 77 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +78 78 | +79 79 | // case with a jump (i.e. a non-nullish prop) +80 | - foo && foo.bar && foo.bar.baz.buzz() && bing + 80 | + foo?.bar?.baz.buzz() && bing +81 81 | foo.bar && foo.bar.baz.buzz() && bing +82 82 | +83 83 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:82:1 + │ +82 │ foo.bar && foo.bar.baz.buzz() && bing + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -79,7 +79,7 @@ +78 78 | +79 79 | // case with a jump (i.e. a non-nullish prop) +80 80 | foo && foo.bar && foo.bar.baz.buzz() && bing +81 | - foo.bar && foo.bar.baz.buzz() && bing + 81 | + foo.bar?.baz.buzz() && bing +82 82 | +83 83 | // case with a jump (i.e. a non-nullish prop) +84 84 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:85:1 + │ +85 │ foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + │ -------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -82,7 +82,7 @@ +81 81 | foo.bar && foo.bar.baz.buzz() && bing +82 82 | +83 83 | // case with a jump (i.e. a non-nullish prop) +84 | - foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing + 84 | + foo?.bar?.baz.buzz?.() && bing +85 85 | +86 86 | // case with a call expr inside the chain for some inefficient reason +87 87 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:88:1 + │ +88 │ foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing + │ ------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -85,7 +85,7 @@ +84 84 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing +85 85 | +86 86 | // case with a call expr inside the chain for some inefficient reason +87 | - foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing + 87 | + foo?.bar()?.baz?.buzz?.() && bing +88 88 | +89 89 | // chained calls with element access +90 90 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:91:1 + │ +91 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -88,7 +88,7 @@ +87 87 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing +88 88 | +89 89 | // chained calls with element access +90 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing + 90 | + foo?.bar?.baz?.[buzz]() && bing +91 91 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing +92 92 | +93 93 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:92:1 + │ +92 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing + │ ------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -89,7 +89,7 @@ +88 88 | +89 89 | // chained calls with element access +90 90 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing +91 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing + 91 | + foo?.bar?.baz?.[buzz]?.() && bing +92 92 | +93 93 | // (partially) pre-optional chained +94 94 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:95:1 + │ +95 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing + │ ----------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -92,7 +92,7 @@ +91 91 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing +92 92 | +93 93 | // (partially) pre-optional chained +94 | - foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing + 94 | + foo?.bar?.baz?.[buzz]?.() && bing +95 95 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing +96 96 | foo && foo?.() && foo?.().bar && bing +97 97 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:96:1 + │ +96 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -93,7 +93,7 @@ +92 92 | +93 93 | // (partially) pre-optional chained +94 94 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing +95 | - foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing + 95 | + foo?.bar.baz?.[buzz] && bing +96 96 | foo && foo?.() && foo?.().bar && bing +97 97 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing +98 98 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:97:1 + │ +97 │ foo && foo?.() && foo?.().bar && bing + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -94,7 +94,7 @@ +93 93 | // (partially) pre-optional chained +94 94 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing +95 95 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing +96 | - foo && foo?.() && foo?.().bar && bing + 96 | + foo?.()?.bar && bing +97 97 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing +98 98 | +99 99 | // chained members + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:98:1 + │ +98 │ foo.bar && foo.bar?.() && foo.bar?.().baz && bing + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -95,7 +95,7 @@ +94 94 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing +95 95 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing +96 96 | foo && foo?.() && foo?.().bar && bing +97 | - foo.bar && foo.bar?.() && foo.bar?.().baz && bing + 97 | + foo.bar?.()?.baz && bing +98 98 | +99 99 | // chained members +100 100 | foo && foo.bar && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:101:1 + │ +101 │ foo && foo.bar && bing.bong + │ -------------- + +Suggested fix: Change to an optional chain. + | @@ -98,7 +98,7 @@ + 97 97 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing + 98 98 | + 99 99 | // chained members +100 | - foo && foo.bar && bing.bong + 100 | + foo?.bar && bing.bong +101 101 | foo.bar && foo.bar.baz && bing.bong +102 102 | foo && foo() && bing.bong +103 103 | foo.bar && foo.bar() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:102:1 + │ +102 │ foo.bar && foo.bar.baz && bing.bong + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -99,7 +99,7 @@ + 98 98 | + 99 99 | // chained members +100 100 | foo && foo.bar && bing.bong +101 | - foo.bar && foo.bar.baz && bing.bong + 101 | + foo.bar?.baz && bing.bong +102 102 | foo && foo() && bing.bong +103 103 | foo.bar && foo.bar() && bing.bong +104 104 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:103:1 + │ +103 │ foo && foo() && bing.bong + │ ------------ + +Suggested fix: Change to an optional chain. + | @@ -100,7 +100,7 @@ + 99 99 | // chained members +100 100 | foo && foo.bar && bing.bong +101 101 | foo.bar && foo.bar.baz && bing.bong +102 | - foo && foo() && bing.bong + 102 | + foo?.() && bing.bong +103 103 | foo.bar && foo.bar() && bing.bong +104 104 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +105 105 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:104:1 + │ +104 │ foo.bar && foo.bar() && bing.bong + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -101,7 +101,7 @@ +100 100 | foo && foo.bar && bing.bong +101 101 | foo.bar && foo.bar.baz && bing.bong +102 102 | foo && foo() && bing.bong +103 | - foo.bar && foo.bar() && bing.bong + 103 | + foo.bar?.() && bing.bong +104 104 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +105 105 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +106 106 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:105:1 + │ +105 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + │ ------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -102,7 +102,7 @@ +101 101 | foo.bar && foo.bar.baz && bing.bong +102 102 | foo && foo() && bing.bong +103 103 | foo.bar && foo.bar() && bing.bong +104 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + 104 | + foo?.bar?.baz?.buzz && bing.bong +105 105 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +106 106 | +107 107 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:106:1 + │ +106 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + │ ------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -103,7 +103,7 @@ +102 102 | foo && foo() && bing.bong +103 103 | foo.bar && foo.bar() && bing.bong +104 104 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +105 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong + 105 | + foo.bar?.baz?.buzz && bing.bong +106 106 | +107 107 | // case with a jump (i.e. a non-nullish prop) +108 108 | foo && foo.bar && foo.bar.baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:109:1 + │ +109 │ foo && foo.bar && foo.bar.baz.buzz && bing.bong + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -106,7 +106,7 @@ +105 105 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && bing.bong +106 106 | +107 107 | // case with a jump (i.e. a non-nullish prop) +108 | - foo && foo.bar && foo.bar.baz.buzz && bing.bong + 108 | + foo?.bar?.baz.buzz && bing.bong +109 109 | foo.bar && foo.bar.baz.buzz && bing.bong +110 110 | +111 111 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:110:1 + │ +110 │ foo.bar && foo.bar.baz.buzz && bing.bong + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -107,7 +107,7 @@ +106 106 | +107 107 | // case with a jump (i.e. a non-nullish prop) +108 108 | foo && foo.bar && foo.bar.baz.buzz && bing.bong +109 | - foo.bar && foo.bar.baz.buzz && bing.bong + 109 | + foo.bar?.baz.buzz && bing.bong +110 110 | +111 111 | // case where for some reason there is a doubled up expression +112 112 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:113:1 + │ +113 │ foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -110,7 +110,7 @@ +109 109 | foo.bar && foo.bar.baz.buzz && bing.bong +110 110 | +111 111 | // case where for some reason there is a doubled up expression +112 | - foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + 112 | + foo?.bar?.baz?.buzz && bing.bong +113 113 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong +114 114 | +115 115 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:114:1 + │ +114 │ foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -111,7 +111,7 @@ +110 110 | +111 111 | // case where for some reason there is a doubled up expression +112 112 | foo && foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong +113 | - foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong + 113 | + foo.bar?.baz?.buzz && bing.bong +114 114 | +115 115 | // chained members with element access +116 116 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:117:1 + │ +117 │ foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -114,7 +114,7 @@ +113 113 | foo.bar && foo.bar.baz && foo.bar.baz && foo.bar.baz.buzz && bing.bong +114 114 | +115 115 | // chained members with element access +116 | - foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong + 116 | + foo?.[bar]?.baz?.buzz && bing.bong +117 117 | +118 118 | // case with a jump (i.e. a non-nullish prop) +119 119 | foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:120:1 + │ +120 │ foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong + │ ---------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -117,7 +117,7 @@ +116 116 | foo && foo[bar] && foo[bar].baz && foo[bar].baz.buzz && bing.bong +117 117 | +118 118 | // case with a jump (i.e. a non-nullish prop) +119 | - foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong + 119 | + foo?.[bar].baz?.buzz && bing.bong +120 120 | +121 121 | // chained calls +122 122 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:123:1 + │ +123 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + │ --------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -120,7 +120,7 @@ +119 119 | foo && foo[bar].baz && foo[bar].baz.buzz && bing.bong +120 120 | +121 121 | // chained calls +122 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong + 122 | + foo?.bar?.baz?.buzz() && bing.bong +123 123 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +124 124 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +125 125 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:124:1 + │ +124 │ foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ ----------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -121,7 +121,7 @@ +120 120 | +121 121 | // chained calls +122 122 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong +123 | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 123 | + foo?.bar?.baz?.buzz?.() && bing.bong +124 124 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +125 125 | +126 126 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:125:1 + │ +125 │ foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -122,7 +122,7 @@ +121 121 | // chained calls +122 122 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz() && bing.bong +123 123 | foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +124 | - foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 124 | + foo.bar?.baz?.buzz?.() && bing.bong +125 125 | +126 126 | // case with a jump (i.e. a non-nullish prop) +127 127 | foo && foo.bar && foo.bar.baz.buzz() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:128:1 + │ +128 │ foo && foo.bar && foo.bar.baz.buzz() && bing.bong + │ ------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -125,7 +125,7 @@ +124 124 | foo.bar && foo.bar.baz && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +125 125 | +126 126 | // case with a jump (i.e. a non-nullish prop) +127 | - foo && foo.bar && foo.bar.baz.buzz() && bing.bong + 127 | + foo?.bar?.baz.buzz() && bing.bong +128 128 | foo.bar && foo.bar.baz.buzz() && bing.bong +129 129 | +130 130 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:129:1 + │ +129 │ foo.bar && foo.bar.baz.buzz() && bing.bong + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -126,7 +126,7 @@ +125 125 | +126 126 | // case with a jump (i.e. a non-nullish prop) +127 127 | foo && foo.bar && foo.bar.baz.buzz() && bing.bong +128 | - foo.bar && foo.bar.baz.buzz() && bing.bong + 128 | + foo.bar?.baz.buzz() && bing.bong +129 129 | +130 130 | // case with a jump (i.e. a non-nullish prop) +131 131 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:132:1 + │ +132 │ foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + │ -------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -129,7 +129,7 @@ +128 128 | foo.bar && foo.bar.baz.buzz() && bing.bong +129 129 | +130 130 | // case with a jump (i.e. a non-nullish prop) +131 | - foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong + 131 | + foo?.bar?.baz.buzz?.() && bing.bong +132 132 | +133 133 | // case with a call expr inside the chain for some inefficient reason +134 134 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:135:1 + │ +135 │ foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + │ ------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -132,7 +132,7 @@ +131 131 | foo && foo.bar && foo.bar.baz.buzz && foo.bar.baz.buzz() && bing.bong +132 132 | +133 133 | // case with a call expr inside the chain for some inefficient reason +134 | - foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong + 134 | + foo?.bar()?.baz?.buzz?.() && bing.bong +135 135 | +136 136 | // chained calls with element access +137 137 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:138:1 + │ +138 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -135,7 +135,7 @@ +134 134 | foo && foo.bar() && foo.bar().baz && foo.bar().baz.buzz && foo.bar().baz.buzz() && bing.bong +135 135 | +136 136 | // chained calls with element access +137 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong + 137 | + foo?.bar?.baz?.[buzz]() && bing.bong +138 138 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong +139 139 | +140 140 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:139:1 + │ +139 │ foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + │ ------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -136,7 +136,7 @@ +135 135 | +136 136 | // chained calls with element access +137 137 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz]() && bing.bong +138 | - foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong + 138 | + foo?.bar?.baz?.[buzz]?.() && bing.bong +139 139 | +140 140 | // (partially) pre-optional chained +141 141 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:142:1 + │ +142 │ foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + │ ----------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -139,7 +139,7 @@ +138 138 | foo && foo.bar && foo.bar.baz && foo.bar.baz[buzz] && foo.bar.baz[buzz]() && bing.bong +139 139 | +140 140 | // (partially) pre-optional chained +141 | - foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong + 141 | + foo?.bar?.baz?.[buzz]?.() && bing.bong +142 142 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong +143 143 | foo && foo?.() && foo?.().bar && bing.bong +144 144 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:143:1 + │ +143 │ foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -140,7 +140,7 @@ +139 139 | +140 140 | // (partially) pre-optional chained +141 141 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong +142 | - foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong + 142 | + foo?.bar.baz?.[buzz] && bing.bong +143 143 | foo && foo?.() && foo?.().bar && bing.bong +144 144 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong +145 145 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:144:1 + │ +144 │ foo && foo?.() && foo?.().bar && bing.bong + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -141,7 +141,7 @@ +140 140 | // (partially) pre-optional chained +141 141 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong +142 142 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong +143 | - foo && foo?.() && foo?.().bar && bing.bong + 143 | + foo?.()?.bar && bing.bong +144 144 | foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong +145 145 | +146 146 | // strict nullish equality checks x !== null && x.y !== null + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:145:1 + │ +145 │ foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -142,7 +142,7 @@ +141 141 | foo && foo?.bar && foo?.bar.baz && foo?.bar.baz[buzz] && foo?.bar.baz[buzz]() && bing.bong +142 142 | foo && foo?.bar.baz && foo?.bar.baz[buzz] && bing.bong +143 143 | foo && foo?.() && foo?.().bar && bing.bong +144 | - foo.bar && foo.bar?.() && foo.bar?.().baz && bing.bong + 144 | + foo.bar?.()?.baz && bing.bong +145 145 | +146 146 | // strict nullish equality checks x !== null && x.y !== null +147 147 | // chained members + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:149:1 + │ +149 │ foo !== null && foo.bar + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -146,7 +146,7 @@ +145 145 | +146 146 | // strict nullish equality checks x !== null && x.y !== null +147 147 | // chained members +148 | - foo !== null && foo.bar + 148 | + foo?.bar +149 149 | foo.bar !== null && foo.bar.baz +150 150 | foo !== null && foo() +151 151 | foo.bar !== null && foo.bar() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:150:1 + │ +150 │ foo.bar !== null && foo.bar.baz + │ ------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -147,7 +147,7 @@ +146 146 | // strict nullish equality checks x !== null && x.y !== null +147 147 | // chained members +148 148 | foo !== null && foo.bar +149 | - foo.bar !== null && foo.bar.baz + 149 | + foo.bar?.baz +150 150 | foo !== null && foo() +151 151 | foo.bar !== null && foo.bar() +152 152 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:151:1 + │ +151 │ foo !== null && foo() + │ --------------------- + +Suggested fix: Change to an optional chain. + | @@ -148,7 +148,7 @@ +147 147 | // chained members +148 148 | foo !== null && foo.bar +149 149 | foo.bar !== null && foo.bar.baz +150 | - foo !== null && foo() + 150 | + foo?.() +151 151 | foo.bar !== null && foo.bar() +152 152 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +153 153 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:152:1 + │ +152 │ foo.bar !== null && foo.bar() + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -149,7 +149,7 @@ +148 148 | foo !== null && foo.bar +149 149 | foo.bar !== null && foo.bar.baz +150 150 | foo !== null && foo() +151 | - foo.bar !== null && foo.bar() + 151 | + foo.bar?.() +152 152 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +153 153 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +154 154 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:153:1 + │ +153 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + │ ---------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -150,7 +150,7 @@ +149 149 | foo.bar !== null && foo.bar.baz +150 150 | foo !== null && foo() +151 151 | foo.bar !== null && foo.bar() +152 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + 152 | + foo?.bar?.baz?.buzz +153 153 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +154 154 | +155 155 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:154:1 + │ +154 │ foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + │ ------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -151,7 +151,7 @@ +150 150 | foo !== null && foo() +151 151 | foo.bar !== null && foo.bar() +152 152 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +153 | - foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz + 153 | + foo.bar?.baz?.buzz +154 154 | +155 155 | // case with a jump (i.e. a non-nullish prop) +156 156 | foo !== null && foo.bar !== null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:157:1 + │ +157 │ foo !== null && foo.bar !== null && foo.bar.baz.buzz + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -154,7 +154,7 @@ +153 153 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz +154 154 | +155 155 | // case with a jump (i.e. a non-nullish prop) +156 | - foo !== null && foo.bar !== null && foo.bar.baz.buzz + 156 | + foo?.bar?.baz.buzz +157 157 | foo.bar !== null && foo.bar.baz.buzz +158 158 | +159 159 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:158:1 + │ +158 │ foo.bar !== null && foo.bar.baz.buzz + │ ------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -155,7 +155,7 @@ +154 154 | +155 155 | // case with a jump (i.e. a non-nullish prop) +156 156 | foo !== null && foo.bar !== null && foo.bar.baz.buzz +157 | - foo.bar !== null && foo.bar.baz.buzz + 157 | + foo.bar?.baz.buzz +158 158 | +159 159 | // case where for some reason there is a doubled up expression +160 160 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:161:1 + │ +161 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + │ ---------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -158,7 +158,7 @@ +157 157 | foo.bar !== null && foo.bar.baz.buzz +158 158 | +159 159 | // case where for some reason there is a doubled up expression +160 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + 160 | + foo?.bar?.baz?.buzz +161 161 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz +162 162 | +163 163 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:162:1 + │ +162 │ foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + │ ------------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -159,7 +159,7 @@ +158 158 | +159 159 | // case where for some reason there is a doubled up expression +160 160 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz +161 | - foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz + 161 | + foo.bar?.baz?.buzz +162 162 | +163 163 | // chained members with element access +164 164 | foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:165:1 + │ +165 │ foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz + │ ------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -162,7 +162,7 @@ +161 161 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz !== null && foo.bar.baz.buzz +162 162 | +163 163 | // chained members with element access +164 | - foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz + 164 | + foo?.[bar]?.baz?.buzz +165 165 | +166 166 | // case with a jump (i.e. a non-nullish prop) +167 167 | foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:168:1 + │ +168 │ foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz + │ ---------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -165,7 +165,7 @@ +164 164 | foo !== null && foo[bar] !== null && foo[bar].baz !== null && foo[bar].baz.buzz +165 165 | +166 166 | // case with a jump (i.e. a non-nullish prop) +167 | - foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz + 167 | + foo?.[bar].baz?.buzz +168 168 | +169 169 | // chained calls +170 170 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:171:1 + │ +171 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -168,7 +168,7 @@ +167 167 | foo !== null && foo[bar].baz !== null && foo[bar].baz.buzz +168 168 | +169 169 | // chained calls +170 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() + 170 | + foo?.bar?.baz?.buzz() +171 171 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +172 172 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +173 173 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:172:1 + │ +172 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + │ ----------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -169,7 +169,7 @@ +168 168 | +169 169 | // chained calls +170 170 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() +171 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + 171 | + foo?.bar?.baz?.buzz?.() +172 172 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +173 173 | +174 174 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:173:1 + │ +173 │ foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -170,7 +170,7 @@ +169 169 | // chained calls +170 170 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz() +171 171 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +172 | - foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + 172 | + foo.bar?.baz?.buzz?.() +173 173 | +174 174 | // case with a jump (i.e. a non-nullish prop) +175 175 | foo !== null && foo.bar !== null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:176:1 + │ +176 │ foo !== null && foo.bar !== null && foo.bar.baz.buzz() + │ ------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -173,7 +173,7 @@ +172 172 | foo.bar !== null && foo.bar.baz !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +173 173 | +174 174 | // case with a jump (i.e. a non-nullish prop) +175 | - foo !== null && foo.bar !== null && foo.bar.baz.buzz() + 175 | + foo?.bar?.baz.buzz() +176 176 | foo.bar !== null && foo.bar.baz.buzz() +177 177 | +178 178 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:177:1 + │ +177 │ foo.bar !== null && foo.bar.baz.buzz() + │ -------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -174,7 +174,7 @@ +173 173 | +174 174 | // case with a jump (i.e. a non-nullish prop) +175 175 | foo !== null && foo.bar !== null && foo.bar.baz.buzz() +176 | - foo.bar !== null && foo.bar.baz.buzz() + 176 | + foo.bar?.baz.buzz() +177 177 | +178 178 | // case with a jump (i.e. a non-nullish prop) +179 179 | foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:180:1 + │ +180 │ foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + │ ----------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -177,7 +177,7 @@ +176 176 | foo.bar !== null && foo.bar.baz.buzz() +177 177 | +178 178 | // case with a jump (i.e. a non-nullish prop) +179 | - foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() + 179 | + foo?.bar?.baz.buzz?.() +180 180 | +181 181 | // case with a call expr inside the chain for some inefficient reason +182 182 | foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:183:1 + │ +183 │ foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() + │ ------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -180,7 +180,7 @@ +179 179 | foo !== null && foo.bar !== null && foo.bar.baz.buzz !== null && foo.bar.baz.buzz() +180 180 | +181 181 | // case with a call expr inside the chain for some inefficient reason +182 | - foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() + 182 | + foo?.bar()?.baz?.buzz?.() +183 183 | +184 184 | // chained calls with element access +185 185 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:186:1 + │ +186 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() + │ ------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -183,7 +183,7 @@ +182 182 | foo !== null && foo.bar() !== null && foo.bar().baz !== null && foo.bar().baz.buzz !== null && foo.bar().baz.buzz() +183 183 | +184 184 | // chained calls with element access +185 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() + 185 | + foo?.bar?.baz?.[buzz]() +186 186 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() +187 187 | +188 188 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:187:1 + │ +187 │ foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() + │ ------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -184,7 +184,7 @@ +183 183 | +184 184 | // chained calls with element access +185 185 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz]() +186 | - foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() + 186 | + foo?.bar?.baz?.[buzz]?.() +187 187 | +188 188 | // (partially) pre-optional chained +189 189 | foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:190:1 + │ +190 │ foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() + │ ----------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -187,7 +187,7 @@ +186 186 | foo !== null && foo.bar !== null && foo.bar.baz !== null && foo.bar.baz[buzz] !== null && foo.bar.baz[buzz]() +187 187 | +188 188 | // (partially) pre-optional chained +189 | - foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() + 189 | + foo?.bar?.baz?.[buzz]?.() +190 190 | foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] +191 191 | foo !== null && foo?.() !== null && foo?.().bar +192 192 | foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:191:1 + │ +191 │ foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] + │ ----------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -188,7 +188,7 @@ +187 187 | +188 188 | // (partially) pre-optional chained +189 189 | foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() +190 | - foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] + 190 | + foo?.bar.baz?.[buzz] +191 191 | foo !== null && foo?.() !== null && foo?.().bar +192 192 | foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz +193 193 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:192:1 + │ +192 │ foo !== null && foo?.() !== null && foo?.().bar + │ ----------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -189,7 +189,7 @@ +188 188 | // (partially) pre-optional chained +189 189 | foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() +190 190 | foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] +191 | - foo !== null && foo?.() !== null && foo?.().bar + 191 | + foo?.()?.bar +192 192 | foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz +193 193 | +194 194 | // chained members + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:193:1 + │ +193 │ foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz + │ ----------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -190,7 +190,7 @@ +189 189 | foo !== null && foo?.bar !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] !== null && foo?.bar.baz[buzz]() +190 190 | foo !== null && foo?.bar.baz !== null && foo?.bar.baz[buzz] +191 191 | foo !== null && foo?.() !== null && foo?.().bar +192 | - foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz + 192 | + foo.bar?.()?.baz +193 193 | +194 194 | // chained members +195 195 | foo !== undefined && foo.bar + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:196:1 + │ +196 │ foo !== undefined && foo.bar + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -193,7 +193,7 @@ +192 192 | foo.bar !== null && foo.bar?.() !== null && foo.bar?.().baz +193 193 | +194 194 | // chained members +195 | - foo !== undefined && foo.bar + 195 | + foo?.bar +196 196 | foo.bar !== undefined && foo.bar.baz +197 197 | foo !== undefined && foo() +198 198 | foo.bar !== undefined && foo.bar() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:197:1 + │ +197 │ foo.bar !== undefined && foo.bar.baz + │ ------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -194,7 +194,7 @@ +193 193 | +194 194 | // chained members +195 195 | foo !== undefined && foo.bar +196 | - foo.bar !== undefined && foo.bar.baz + 196 | + foo.bar?.baz +197 197 | foo !== undefined && foo() +198 198 | foo.bar !== undefined && foo.bar() +199 199 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:198:1 + │ +198 │ foo !== undefined && foo() + │ -------------------------- + +Suggested fix: Change to an optional chain. + | @@ -195,7 +195,7 @@ +194 194 | // chained members +195 195 | foo !== undefined && foo.bar +196 196 | foo.bar !== undefined && foo.bar.baz +197 | - foo !== undefined && foo() + 197 | + foo?.() +198 198 | foo.bar !== undefined && foo.bar() +199 199 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +200 200 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:199:1 + │ +199 │ foo.bar !== undefined && foo.bar() + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -196,7 +196,7 @@ +195 195 | foo !== undefined && foo.bar +196 196 | foo.bar !== undefined && foo.bar.baz +197 197 | foo !== undefined && foo() +198 | - foo.bar !== undefined && foo.bar() + 198 | + foo.bar?.() +199 199 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +200 200 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +201 201 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:200:1 + │ +200 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + │ ------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -197,7 +197,7 @@ +196 196 | foo.bar !== undefined && foo.bar.baz +197 197 | foo !== undefined && foo() +198 198 | foo.bar !== undefined && foo.bar() +199 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + 199 | + foo?.bar?.baz?.buzz +200 200 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +201 201 | +202 202 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:201:1 + │ +201 │ foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + │ ---------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -198,7 +198,7 @@ +197 197 | foo !== undefined && foo() +198 198 | foo.bar !== undefined && foo.bar() +199 199 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +200 | - foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + 200 | + foo.bar?.baz?.buzz +201 201 | +202 202 | // case with a jump (i.e. a non-nullish prop) +203 203 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:204:1 + │ +204 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz + │ -------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -201,7 +201,7 @@ +200 200 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +201 201 | +202 202 | // case with a jump (i.e. a non-nullish prop) +203 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz + 203 | + foo?.bar?.baz.buzz +204 204 | foo.bar !== undefined && foo.bar.baz.buzz +205 205 | +206 206 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:205:1 + │ +205 │ foo.bar !== undefined && foo.bar.baz.buzz + │ ----------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -202,7 +202,7 @@ +201 201 | +202 202 | // case with a jump (i.e. a non-nullish prop) +203 203 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz +204 | - foo.bar !== undefined && foo.bar.baz.buzz + 204 | + foo.bar?.baz.buzz +205 205 | +206 206 | // case where for some reason there is a doubled up expression +207 207 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:208:1 + │ +208 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + │ ------------------------------------------------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -205,7 +205,7 @@ +204 204 | foo.bar !== undefined && foo.bar.baz.buzz +205 205 | +206 206 | // case where for some reason there is a doubled up expression +207 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + 207 | + foo?.bar?.baz?.buzz +208 208 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +209 209 | +210 210 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:209:1 + │ +209 │ foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + │ --------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -206,7 +206,7 @@ +205 205 | +206 206 | // case where for some reason there is a doubled up expression +207 207 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +208 | - foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz + 208 | + foo.bar?.baz?.buzz +209 209 | +210 210 | // chained members with element access +211 211 | foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:212:1 + │ +212 │ foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + │ ---------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -209,7 +209,7 @@ +208 208 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz +209 209 | +210 210 | // chained members with element access +211 | - foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + 211 | + foo?.[bar]?.baz?.buzz +212 212 | +213 213 | // case with a jump (i.e. a non-nullish prop) +214 214 | foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:215:1 + │ +215 │ foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + │ -------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -212,7 +212,7 @@ +211 211 | foo !== undefined && foo[bar] !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz +212 212 | +213 213 | // case with a jump (i.e. a non-nullish prop) +214 | - foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz + 214 | + foo?.[bar].baz?.buzz +215 215 | +216 216 | // chained calls +217 217 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:218:1 + │ +218 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() + │ --------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -215,7 +215,7 @@ +214 214 | foo !== undefined && foo[bar].baz !== undefined && foo[bar].baz.buzz +215 215 | +216 216 | // chained calls +217 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() + 217 | + foo?.bar?.baz?.buzz() +218 218 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +219 219 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +220 220 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:219:1 + │ +219 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -216,7 +216,7 @@ +215 215 | +216 216 | // chained calls +217 217 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() +218 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + 218 | + foo?.bar?.baz?.buzz?.() +219 219 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +220 220 | +221 221 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:220:1 + │ +220 │ foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + │ ---------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -217,7 +217,7 @@ +216 216 | // chained calls +217 217 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz() +218 218 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +219 | - foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + 219 | + foo.bar?.baz?.buzz?.() +220 220 | +221 221 | // case with a jump (i.e. a non-nullish prop) +222 222 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:223:1 + │ +223 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() + │ ---------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -220,7 +220,7 @@ +219 219 | foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +220 220 | +221 221 | // case with a jump (i.e. a non-nullish prop) +222 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() + 222 | + foo?.bar?.baz.buzz() +223 223 | foo.bar !== undefined && foo.bar.baz.buzz() +224 224 | +225 225 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:224:1 + │ +224 │ foo.bar !== undefined && foo.bar.baz.buzz() + │ ------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -221,7 +221,7 @@ +220 220 | +221 221 | // case with a jump (i.e. a non-nullish prop) +222 222 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz() +223 | - foo.bar !== undefined && foo.bar.baz.buzz() + 223 | + foo.bar?.baz.buzz() +224 224 | +225 225 | // case with a jump (i.e. a non-nullish prop) +226 226 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:227:1 + │ +227 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + │ -------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -224,7 +224,7 @@ +223 223 | foo.bar !== undefined && foo.bar.baz.buzz() +224 224 | +225 225 | // case with a jump (i.e. a non-nullish prop) +226 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() + 226 | + foo?.bar?.baz.buzz?.() +227 227 | +228 228 | // case with a call expr inside the chain for some inefficient reason +229 229 | foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:230:1 + │ +230 │ foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() + │ --------------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -227,7 +227,7 @@ +226 226 | foo !== undefined && foo.bar !== undefined && foo.bar.baz.buzz !== undefined && foo.bar.baz.buzz() +227 227 | +228 228 | // case with a call expr inside the chain for some inefficient reason +229 | - foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() + 229 | + foo?.bar()?.baz?.buzz?.() +230 230 | +231 231 | // chained calls with element access +232 232 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:233:1 + │ +233 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() + │ ---------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -230,7 +230,7 @@ +229 229 | foo !== undefined && foo.bar() !== undefined && foo.bar().baz !== undefined && foo.bar().baz.buzz !== undefined && foo.bar().baz.buzz() +230 230 | +231 231 | // chained calls with element access +232 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() + 232 | + foo?.bar?.baz?.[buzz]() +233 233 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() +234 234 | +235 235 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:234:1 + │ +234 │ foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() + │ --------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -231,7 +231,7 @@ +230 230 | +231 231 | // chained calls with element access +232 232 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz]() +233 | - foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() + 233 | + foo?.bar?.baz?.[buzz]?.() +234 234 | +235 235 | // (partially) pre-optional chained +236 236 | foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:237:1 + │ +237 │ foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() + │ ------------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -234,7 +234,7 @@ +233 233 | foo !== undefined && foo.bar !== undefined && foo.bar.baz !== undefined && foo.bar.baz[buzz] !== undefined && foo.bar.baz[buzz]() +234 234 | +235 235 | // (partially) pre-optional chained +236 | - foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() + 236 | + foo?.bar?.baz?.[buzz]?.() +237 237 | foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] +238 238 | foo !== undefined && foo?.() !== undefined && foo?.().bar +239 239 | foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:238:1 + │ +238 │ foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] + │ --------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -235,7 +235,7 @@ +234 234 | +235 235 | // (partially) pre-optional chained +236 236 | foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() +237 | - foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] + 237 | + foo?.bar.baz?.[buzz] +238 238 | foo !== undefined && foo?.() !== undefined && foo?.().bar +239 239 | foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz +240 240 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:239:1 + │ +239 │ foo !== undefined && foo?.() !== undefined && foo?.().bar + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -236,7 +236,7 @@ +235 235 | // (partially) pre-optional chained +236 236 | foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() +237 237 | foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] +238 | - foo !== undefined && foo?.() !== undefined && foo?.().bar + 238 | + foo?.()?.bar +239 239 | foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz +240 240 | +241 241 | // chained members + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:240:1 + │ +240 │ foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz + │ --------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -237,7 +237,7 @@ +236 236 | foo !== undefined && foo?.bar !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] !== undefined && foo?.bar.baz[buzz]() +237 237 | foo !== undefined && foo?.bar.baz !== undefined && foo?.bar.baz[buzz] +238 238 | foo !== undefined && foo?.() !== undefined && foo?.().bar +239 | - foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz + 239 | + foo.bar?.()?.baz +240 240 | +241 241 | // chained members +242 242 | foo != null && foo.bar + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:243:1 + │ +243 │ foo != null && foo.bar + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -240,7 +240,7 @@ +239 239 | foo.bar !== undefined && foo.bar?.() !== undefined && foo.bar?.().baz +240 240 | +241 241 | // chained members +242 | - foo != null && foo.bar + 242 | + foo?.bar +243 243 | foo.bar != null && foo.bar.baz +244 244 | foo != null && foo() +245 245 | foo.bar != null && foo.bar() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:244:1 + │ +244 │ foo.bar != null && foo.bar.baz + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -241,7 +241,7 @@ +240 240 | +241 241 | // chained members +242 242 | foo != null && foo.bar +243 | - foo.bar != null && foo.bar.baz + 243 | + foo.bar?.baz +244 244 | foo != null && foo() +245 245 | foo.bar != null && foo.bar() +246 246 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:245:1 + │ +245 │ foo != null && foo() + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -242,7 +242,7 @@ +241 241 | // chained members +242 242 | foo != null && foo.bar +243 243 | foo.bar != null && foo.bar.baz +244 | - foo != null && foo() + 244 | + foo?.() +245 245 | foo.bar != null && foo.bar() +246 246 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +247 247 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:246:1 + │ +246 │ foo.bar != null && foo.bar() + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -243,7 +243,7 @@ +242 242 | foo != null && foo.bar +243 243 | foo.bar != null && foo.bar.baz +244 244 | foo != null && foo() +245 | - foo.bar != null && foo.bar() + 245 | + foo.bar?.() +246 246 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +247 247 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +248 248 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:247:1 + │ +247 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + │ ------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -244,7 +244,7 @@ +243 243 | foo.bar != null && foo.bar.baz +244 244 | foo != null && foo() +245 245 | foo.bar != null && foo.bar() +246 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + 246 | + foo?.bar?.baz?.buzz +247 247 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +248 248 | +249 249 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:248:1 + │ +248 │ foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + │ ---------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -245,7 +245,7 @@ +244 244 | foo != null && foo() +245 245 | foo.bar != null && foo.bar() +246 246 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +247 | - foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz + 247 | + foo.bar?.baz?.buzz +248 248 | +249 249 | // case with a jump (i.e. a non-nullish prop) +250 250 | foo != null && foo.bar != null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:251:1 + │ +251 │ foo != null && foo.bar != null && foo.bar.baz.buzz + │ -------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -248,7 +248,7 @@ +247 247 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz +248 248 | +249 249 | // case with a jump (i.e. a non-nullish prop) +250 | - foo != null && foo.bar != null && foo.bar.baz.buzz + 250 | + foo?.bar?.baz.buzz +251 251 | foo.bar != null && foo.bar.baz.buzz +252 252 | +253 253 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:252:1 + │ +252 │ foo.bar != null && foo.bar.baz.buzz + │ ----------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -249,7 +249,7 @@ +248 248 | +249 249 | // case with a jump (i.e. a non-nullish prop) +250 250 | foo != null && foo.bar != null && foo.bar.baz.buzz +251 | - foo.bar != null && foo.bar.baz.buzz + 251 | + foo.bar?.baz.buzz +252 252 | +253 253 | // case where for some reason there is a doubled up expression +254 254 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:255:1 + │ +255 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + │ ------------------------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -252,7 +252,7 @@ +251 251 | foo.bar != null && foo.bar.baz.buzz +252 252 | +253 253 | // case where for some reason there is a doubled up expression +254 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + 254 | + foo?.bar?.baz?.buzz +255 255 | foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz +256 256 | +257 257 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:256:1 + │ +256 │ foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + │ --------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -253,7 +253,7 @@ +252 252 | +253 253 | // case where for some reason there is a doubled up expression +254 254 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz +255 | - foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz + 255 | + foo.bar?.baz?.buzz +256 256 | +257 257 | // chained members with element access +258 258 | foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:259:1 + │ +259 │ foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz + │ ---------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -256,7 +256,7 @@ +255 255 | foo.bar != null && foo.bar.baz != null && foo.bar.baz != null && foo.bar.baz.buzz +256 256 | +257 257 | // chained members with element access +258 | - foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz + 258 | + foo?.[bar]?.baz?.buzz +259 259 | +260 260 | // case with a jump (i.e. a non-nullish prop) +261 261 | foo != null && foo[bar].baz != null && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:262:1 + │ +262 │ foo != null && foo[bar].baz != null && foo[bar].baz.buzz + │ -------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -259,7 +259,7 @@ +258 258 | foo != null && foo[bar] != null && foo[bar].baz != null && foo[bar].baz.buzz +259 259 | +260 260 | // case with a jump (i.e. a non-nullish prop) +261 | - foo != null && foo[bar].baz != null && foo[bar].baz.buzz + 261 | + foo?.[bar].baz?.buzz +262 262 | +263 263 | // chained calls +264 264 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:265:1 + │ +265 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() + │ --------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -262,7 +262,7 @@ +261 261 | foo != null && foo[bar].baz != null && foo[bar].baz.buzz +262 262 | +263 263 | // chained calls +264 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() + 264 | + foo?.bar?.baz?.buzz() +265 265 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +266 266 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +267 267 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:266:1 + │ +266 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -263,7 +263,7 @@ +262 262 | +263 263 | // chained calls +264 264 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() +265 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + 265 | + foo?.bar?.baz?.buzz?.() +266 266 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +267 267 | +268 268 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:267:1 + │ +267 │ foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + │ ---------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -264,7 +264,7 @@ +263 263 | // chained calls +264 264 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz() +265 265 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +266 | - foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + 266 | + foo.bar?.baz?.buzz?.() +267 267 | +268 268 | // case with a jump (i.e. a non-nullish prop) +269 269 | foo != null && foo.bar != null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:270:1 + │ +270 │ foo != null && foo.bar != null && foo.bar.baz.buzz() + │ ---------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -267,7 +267,7 @@ +266 266 | foo.bar != null && foo.bar.baz != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +267 267 | +268 268 | // case with a jump (i.e. a non-nullish prop) +269 | - foo != null && foo.bar != null && foo.bar.baz.buzz() + 269 | + foo?.bar?.baz.buzz() +270 270 | foo.bar != null && foo.bar.baz.buzz() +271 271 | +272 272 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:271:1 + │ +271 │ foo.bar != null && foo.bar.baz.buzz() + │ ------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -268,7 +268,7 @@ +267 267 | +268 268 | // case with a jump (i.e. a non-nullish prop) +269 269 | foo != null && foo.bar != null && foo.bar.baz.buzz() +270 | - foo.bar != null && foo.bar.baz.buzz() + 270 | + foo.bar?.baz.buzz() +271 271 | +272 272 | // case with a jump (i.e. a non-nullish prop) +273 273 | foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:274:1 + │ +274 │ foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + │ -------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -271,7 +271,7 @@ +270 270 | foo.bar != null && foo.bar.baz.buzz() +271 271 | +272 272 | // case with a jump (i.e. a non-nullish prop) +273 | - foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() + 273 | + foo?.bar?.baz.buzz?.() +274 274 | +275 275 | // case with a call expr inside the chain for some inefficient reason +276 276 | foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:277:1 + │ +277 │ foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() + │ --------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -274,7 +274,7 @@ +273 273 | foo != null && foo.bar != null && foo.bar.baz.buzz != null && foo.bar.baz.buzz() +274 274 | +275 275 | // case with a call expr inside the chain for some inefficient reason +276 | - foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() + 276 | + foo?.bar()?.baz?.buzz?.() +277 277 | +278 278 | // chained calls with element access +279 279 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:280:1 + │ +280 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() + │ ---------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -277,7 +277,7 @@ +276 276 | foo != null && foo.bar() != null && foo.bar().baz != null && foo.bar().baz.buzz != null && foo.bar().baz.buzz() +277 277 | +278 278 | // chained calls with element access +279 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() + 279 | + foo?.bar?.baz?.[buzz]() +280 280 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() +281 281 | +282 282 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:281:1 + │ +281 │ foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() + │ --------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -278,7 +278,7 @@ +277 277 | +278 278 | // chained calls with element access +279 279 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz]() +280 | - foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() + 280 | + foo?.bar?.baz?.[buzz]?.() +281 281 | +282 282 | // (partially) pre-optional chained +283 283 | foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:284:1 + │ +284 │ foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() + │ ------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -281,7 +281,7 @@ +280 280 | foo != null && foo.bar != null && foo.bar.baz != null && foo.bar.baz[buzz] != null && foo.bar.baz[buzz]() +281 281 | +282 282 | // (partially) pre-optional chained +283 | - foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() + 283 | + foo?.bar?.baz?.[buzz]?.() +284 284 | foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] +285 285 | foo != null && foo?.() != null && foo?.().bar +286 286 | foo.bar != null && foo.bar?.() != null && foo.bar?.().baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:285:1 + │ +285 │ foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -282,7 +282,7 @@ +281 281 | +282 282 | // (partially) pre-optional chained +283 283 | foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() +284 | - foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] + 284 | + foo?.bar.baz?.[buzz] +285 285 | foo != null && foo?.() != null && foo?.().bar +286 286 | foo.bar != null && foo.bar?.() != null && foo.bar?.().baz +287 287 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:286:1 + │ +286 │ foo != null && foo?.() != null && foo?.().bar + │ --------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -283,7 +283,7 @@ +282 282 | // (partially) pre-optional chained +283 283 | foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() +284 284 | foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] +285 | - foo != null && foo?.() != null && foo?.().bar + 285 | + foo?.()?.bar +286 286 | foo.bar != null && foo.bar?.() != null && foo.bar?.().baz +287 287 | +288 288 | // chained members + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:287:1 + │ +287 │ foo.bar != null && foo.bar?.() != null && foo.bar?.().baz + │ --------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -284,7 +284,7 @@ +283 283 | foo != null && foo?.bar != null && foo?.bar.baz != null && foo?.bar.baz[buzz] != null && foo?.bar.baz[buzz]() +284 284 | foo != null && foo?.bar.baz != null && foo?.bar.baz[buzz] +285 285 | foo != null && foo?.() != null && foo?.().bar +286 | - foo.bar != null && foo.bar?.() != null && foo.bar?.().baz + 286 | + foo.bar?.()?.baz +287 287 | +288 288 | // chained members +289 289 | foo != undefined && foo.bar + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:290:1 + │ +290 │ foo != undefined && foo.bar + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -287,7 +287,7 @@ +286 286 | foo.bar != null && foo.bar?.() != null && foo.bar?.().baz +287 287 | +288 288 | // chained members +289 | - foo != undefined && foo.bar + 289 | + foo?.bar +290 290 | foo.bar != undefined && foo.bar.baz +291 291 | foo != undefined && foo() +292 292 | foo.bar != undefined && foo.bar() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:291:1 + │ +291 │ foo.bar != undefined && foo.bar.baz + │ ----------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -288,7 +288,7 @@ +287 287 | +288 288 | // chained members +289 289 | foo != undefined && foo.bar +290 | - foo.bar != undefined && foo.bar.baz + 290 | + foo.bar?.baz +291 291 | foo != undefined && foo() +292 292 | foo.bar != undefined && foo.bar() +293 293 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:292:1 + │ +292 │ foo != undefined && foo() + │ ------------------------- + +Suggested fix: Change to an optional chain. + | @@ -289,7 +289,7 @@ +288 288 | // chained members +289 289 | foo != undefined && foo.bar +290 290 | foo.bar != undefined && foo.bar.baz +291 | - foo != undefined && foo() + 291 | + foo?.() +292 292 | foo.bar != undefined && foo.bar() +293 293 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +294 294 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:293:1 + │ +293 │ foo.bar != undefined && foo.bar() + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -290,7 +290,7 @@ +289 289 | foo != undefined && foo.bar +290 290 | foo.bar != undefined && foo.bar.baz +291 291 | foo != undefined && foo() +292 | - foo.bar != undefined && foo.bar() + 292 | + foo.bar?.() +293 293 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +294 294 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +295 295 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:294:1 + │ +294 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + │ ---------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -291,7 +291,7 @@ +290 290 | foo.bar != undefined && foo.bar.baz +291 291 | foo != undefined && foo() +292 292 | foo.bar != undefined && foo.bar() +293 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + 293 | + foo?.bar?.baz?.buzz +294 294 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +295 295 | +296 296 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:295:1 + │ +295 │ foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + │ -------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -292,7 +292,7 @@ +291 291 | foo != undefined && foo() +292 292 | foo.bar != undefined && foo.bar() +293 293 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +294 | - foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + 294 | + foo.bar?.baz?.buzz +295 295 | +296 296 | // case with a jump (i.e. a non-nullish prop) +297 297 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:298:1 + │ +298 │ foo != undefined && foo.bar != undefined && foo.bar.baz.buzz + │ ------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -295,7 +295,7 @@ +294 294 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +295 295 | +296 296 | // case with a jump (i.e. a non-nullish prop) +297 | - foo != undefined && foo.bar != undefined && foo.bar.baz.buzz + 297 | + foo?.bar?.baz.buzz +298 298 | foo.bar != undefined && foo.bar.baz.buzz +299 299 | +300 300 | // case where for some reason there is a doubled up expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:299:1 + │ +299 │ foo.bar != undefined && foo.bar.baz.buzz + │ ---------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -296,7 +296,7 @@ +295 295 | +296 296 | // case with a jump (i.e. a non-nullish prop) +297 297 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz +298 | - foo.bar != undefined && foo.bar.baz.buzz + 298 | + foo.bar?.baz.buzz +299 299 | +300 300 | // case where for some reason there is a doubled up expression +301 301 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:302:1 + │ +302 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + │ -------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -299,7 +299,7 @@ +298 298 | foo.bar != undefined && foo.bar.baz.buzz +299 299 | +300 300 | // case where for some reason there is a doubled up expression +301 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + 301 | + foo?.bar?.baz?.buzz +302 302 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +303 303 | +304 304 | // chained members with element access + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:303:1 + │ +303 │ foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + │ ------------------------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -300,7 +300,7 @@ +299 299 | +300 300 | // case where for some reason there is a doubled up expression +301 301 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +302 | - foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz + 302 | + foo.bar?.baz?.buzz +303 303 | +304 304 | // chained members with element access +305 305 | foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:306:1 + │ +306 │ foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + │ ------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -303,7 +303,7 @@ +302 302 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz +303 303 | +304 304 | // chained members with element access +305 | - foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + 305 | + foo?.[bar]?.baz?.buzz +306 306 | +307 307 | // case with a jump (i.e. a non-nullish prop) +308 308 | foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:309:1 + │ +309 │ foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + │ ------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -306,7 +306,7 @@ +305 305 | foo != undefined && foo[bar] != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz +306 306 | +307 307 | // case with a jump (i.e. a non-nullish prop) +308 | - foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz + 308 | + foo?.[bar].baz?.buzz +309 309 | +310 310 | // chained calls +311 311 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:312:1 + │ +312 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -309,7 +309,7 @@ +308 308 | foo != undefined && foo[bar].baz != undefined && foo[bar].baz.buzz +309 309 | +310 310 | // chained calls +311 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() + 311 | + foo?.bar?.baz?.buzz() +312 312 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +313 313 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +314 314 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:313:1 + │ +313 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + │ --------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -310,7 +310,7 @@ +309 309 | +310 310 | // chained calls +311 311 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() +312 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + 312 | + foo?.bar?.baz?.buzz?.() +313 313 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +314 314 | +315 315 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:314:1 + │ +314 │ foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + │ ------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -311,7 +311,7 @@ +310 310 | // chained calls +311 311 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz() +312 312 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +313 | - foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + 313 | + foo.bar?.baz?.buzz?.() +314 314 | +315 315 | // case with a jump (i.e. a non-nullish prop) +316 316 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:317:1 + │ +317 │ foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() + │ -------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -314,7 +314,7 @@ +313 313 | foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +314 314 | +315 315 | // case with a jump (i.e. a non-nullish prop) +316 | - foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() + 316 | + foo?.bar?.baz.buzz() +317 317 | foo.bar != undefined && foo.bar.baz.buzz() +318 318 | +319 319 | // case with a jump (i.e. a non-nullish prop) + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:318:1 + │ +318 │ foo.bar != undefined && foo.bar.baz.buzz() + │ ------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -315,7 +315,7 @@ +314 314 | +315 315 | // case with a jump (i.e. a non-nullish prop) +316 316 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz() +317 | - foo.bar != undefined && foo.bar.baz.buzz() + 317 | + foo.bar?.baz.buzz() +318 318 | +319 319 | // case with a jump (i.e. a non-nullish prop) +320 320 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:321:1 + │ +321 │ foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + │ ----------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -318,7 +318,7 @@ +317 317 | foo.bar != undefined && foo.bar.baz.buzz() +318 318 | +319 319 | // case with a jump (i.e. a non-nullish prop) +320 | - foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() + 320 | + foo?.bar?.baz.buzz?.() +321 321 | +322 322 | // case with a call expr inside the chain for some inefficient reason +323 323 | foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:324:1 + │ +324 │ foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() + │ ----------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -321,7 +321,7 @@ +320 320 | foo != undefined && foo.bar != undefined && foo.bar.baz.buzz != undefined && foo.bar.baz.buzz() +321 321 | +322 322 | // case with a call expr inside the chain for some inefficient reason +323 | - foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() + 323 | + foo?.bar()?.baz?.buzz?.() +324 324 | +325 325 | // chained calls with element access +326 326 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:327:1 + │ +327 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() + │ ------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -324,7 +324,7 @@ +323 323 | foo != undefined && foo.bar() != undefined && foo.bar().baz != undefined && foo.bar().baz.buzz != undefined && foo.bar().baz.buzz() +324 324 | +325 325 | // chained calls with element access +326 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() + 326 | + foo?.bar?.baz?.[buzz]() +327 327 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() +328 328 | +329 329 | // (partially) pre-optional chained + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:328:1 + │ +328 │ foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() + │ ----------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -325,7 +325,7 @@ +324 324 | +325 325 | // chained calls with element access +326 326 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz]() +327 | - foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() + 327 | + foo?.bar?.baz?.[buzz]?.() +328 328 | +329 329 | // (partially) pre-optional chained +330 330 | foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:331:1 + │ +331 │ foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() + │ --------------------------------------------------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -328,7 +328,7 @@ +327 327 | foo != undefined && foo.bar != undefined && foo.bar.baz != undefined && foo.bar.baz[buzz] != undefined && foo.bar.baz[buzz]() +328 328 | +329 329 | // (partially) pre-optional chained +330 | - foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() + 330 | + foo?.bar?.baz?.[buzz]?.() +331 331 | foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] +332 332 | foo != undefined && foo?.() != undefined && foo?.().bar +333 333 | foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:332:1 + │ +332 │ foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] + │ ------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -329,7 +329,7 @@ +328 328 | +329 329 | // (partially) pre-optional chained +330 330 | foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() +331 | - foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] + 331 | + foo?.bar.baz?.[buzz] +332 332 | foo != undefined && foo?.() != undefined && foo?.().bar +333 333 | foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz +334 334 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:333:1 + │ +333 │ foo != undefined && foo?.() != undefined && foo?.().bar + │ ------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -330,7 +330,7 @@ +329 329 | // (partially) pre-optional chained +330 330 | foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() +331 331 | foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] +332 | - foo != undefined && foo?.() != undefined && foo?.().bar + 332 | + foo?.()?.bar +333 333 | foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz +334 334 | +335 335 | //private static member name + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:334:1 + │ +334 │ foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz + │ ------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -331,7 +331,7 @@ +330 330 | foo != undefined && foo?.bar != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] != undefined && foo?.bar.baz[buzz]() +331 331 | foo != undefined && foo?.bar.baz != undefined && foo?.bar.baz[buzz] +332 332 | foo != undefined && foo?.() != undefined && foo?.().bar +333 | - foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz + 333 | + foo.bar?.()?.baz +334 334 | +335 335 | //private static member name +336 336 | foo && foo.#bar + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:337:1 + │ +337 │ foo && foo.#bar + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -334,7 +334,7 @@ +333 333 | foo.bar != undefined && foo.bar?.() != undefined && foo.bar?.().baz +334 334 | +335 335 | //private static member name +336 | - foo && foo.#bar + 336 | + foo?.#bar +337 337 | foo.#bar && foo.#bar.#baz +338 338 | foo.#bar && foo.#bar() +339 339 | foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:338:1 + │ +338 │ foo.#bar && foo.#bar.#baz + │ ------------------------- + +Suggested fix: Change to an optional chain. + | @@ -335,7 +335,7 @@ +334 334 | +335 335 | //private static member name +336 336 | foo && foo.#bar +337 | - foo.#bar && foo.#bar.#baz + 337 | + foo.#bar?.#baz +338 338 | foo.#bar && foo.#bar() +339 339 | foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +340 340 | foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:339:1 + │ +339 │ foo.#bar && foo.#bar() + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -336,7 +336,7 @@ +335 335 | //private static member name +336 336 | foo && foo.#bar +337 337 | foo.#bar && foo.#bar.#baz +338 | - foo.#bar && foo.#bar() + 338 | + foo.#bar?.() +339 339 | foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +340 340 | foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +341 341 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:340:1 + │ +340 │ foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + │ ------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -337,7 +337,7 @@ +336 336 | foo && foo.#bar +337 337 | foo.#bar && foo.#bar.#baz +338 338 | foo.#bar && foo.#bar() +339 | - foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 339 | + foo?.#bar?.#baz?.#buzz +340 340 | foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +341 341 | +342 342 | // two errors + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:341:1 + │ +341 │ foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + │ ------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -338,7 +338,7 @@ +337 337 | foo.#bar && foo.#bar.#baz +338 338 | foo.#bar && foo.#bar() +339 339 | foo && foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +340 | - foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz + 340 | + foo.#bar?.#baz?.#buzz +341 341 | +342 342 | // two errors +343 343 | foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:344:1 + │ +344 │ foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -341,7 +341,7 @@ +340 340 | foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +341 341 | +342 342 | // two errors +343 | - foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + 343 | + foo?.bar?.baz || baz && baz.bar && baz.bar.foo +344 344 | +345 345 | // case with inconsistent checks +346 346 | foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:344:34 + │ +344 │ foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -341,7 +341,7 @@ +340 340 | foo.#bar && foo.#bar.#baz && foo.#bar.#baz.#buzz +341 341 | +342 342 | // two errors +343 | - foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo + 343 | + foo && foo.bar && foo.bar.baz || baz?.bar?.foo +344 344 | +345 345 | // case with inconsistent checks +346 346 | foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:347:1 + │ +347 │ foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + │ ----------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -344,7 +344,7 @@ +343 343 | foo && foo.bar && foo.bar.baz || baz && baz.bar && baz.bar.foo +344 344 | +345 345 | // case with inconsistent checks +346 | - foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; + 346 | + foo?.bar?.baz?.buzz; +347 347 | +348 348 | foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; +349 349 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:349:1 + │ +349 │ foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; + │ --------------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -346,7 +346,7 @@ +345 345 | // case with inconsistent checks +346 346 | foo && foo.bar != null && foo.bar.baz !== undefined && foo.bar.baz.buzz; +347 347 | +348 | - foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; + 348 | + foo.bar?.baz?.qux?.buzz; +349 349 | +350 350 | // ensure essential whitespace isn't removed +351 351 | foo && foo.bar(baz => ); + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:352:1 + │ +352 │ foo && foo.bar(baz => ); + │ ----------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -349,7 +349,7 @@ +348 348 | foo.bar && foo.bar.baz != null && foo.bar.baz.qux !== undefined && foo.bar.baz.qux.buzz; +349 349 | +350 350 | // ensure essential whitespace isn't removed +351 | - foo && foo.bar(baz => ); + 351 | + foo?.bar(baz => ); +352 352 | foo && foo.bar(baz => typeof baz); +353 353 | foo && foo["some long string"] && foo["some long string"].baz +354 354 | foo && foo[`some long string`] && foo[`some long string`].baz + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:353:1 + │ +353 │ foo && foo.bar(baz => typeof baz); + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -350,7 +350,7 @@ +349 349 | +350 350 | // ensure essential whitespace isn't removed +351 351 | foo && foo.bar(baz => ); +352 | - foo && foo.bar(baz => typeof baz); + 352 | + foo?.bar(baz => typeof baz); +353 353 | foo && foo["some long string"] && foo["some long string"].baz +354 354 | foo && foo[`some long string`] && foo[`some long string`].baz +355 355 | foo && foo['some long string'] && foo['some long string'].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:354:1 + │ +354 │ foo && foo["some long string"] && foo["some long string"].baz + │ ------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -351,7 +351,7 @@ +350 350 | // ensure essential whitespace isn't removed +351 351 | foo && foo.bar(baz => ); +352 352 | foo && foo.bar(baz => typeof baz); +353 | - foo && foo["some long string"] && foo["some long string"].baz + 353 | + foo?.["some long string"]?.baz +354 354 | foo && foo[`some long string`] && foo[`some long string`].baz +355 355 | foo && foo['some long string'] && foo['some long string'].baz; +356 356 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:355:1 + │ +355 │ foo && foo[`some long string`] && foo[`some long string`].baz + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -352,7 +352,7 @@ +351 351 | foo && foo.bar(baz => ); +352 352 | foo && foo.bar(baz => typeof baz); +353 353 | foo && foo["some long string"] && foo["some long string"].baz +354 | - foo && foo[`some long string`] && foo[`some long string`].baz + 354 | + foo?.[`some long string`] && foo[`some long string`].baz +355 355 | foo && foo['some long string'] && foo['some long string'].baz; +356 356 | +357 357 | // other literal expressions + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:356:1 + │ +356 │ foo && foo['some long string'] && foo['some long string'].baz; + │ ------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -353,7 +353,7 @@ +352 352 | foo && foo.bar(baz => typeof baz); +353 353 | foo && foo["some long string"] && foo["some long string"].baz +354 354 | foo && foo[`some long string`] && foo[`some long string`].baz +355 | - foo && foo['some long string'] && foo['some long string'].baz; + 355 | + foo?.['some long string']?.baz; +356 356 | +357 357 | // other literal expressions +358 358 | foo && foo[123] && foo[123].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:359:1 + │ +359 │ foo && foo[123] && foo[123].baz; + │ ------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -356,7 +356,7 @@ +355 355 | foo && foo['some long string'] && foo['some long string'].baz; +356 356 | +357 357 | // other literal expressions +358 | - foo && foo[123] && foo[123].baz; + 358 | + foo?.[123]?.baz; +359 359 | foo && foo[true] && foo[true].baz; +360 360 | foo && foo[null] && foo[null].baz; +361 361 | foo && foo[12n] && foo[12n].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:360:1 + │ +360 │ foo && foo[true] && foo[true].baz; + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -357,7 +357,7 @@ +356 356 | +357 357 | // other literal expressions +358 358 | foo && foo[123] && foo[123].baz; +359 | - foo && foo[true] && foo[true].baz; + 359 | + foo?.[true]?.baz; +360 360 | foo && foo[null] && foo[null].baz; +361 361 | foo && foo[12n] && foo[12n].baz; +362 362 | foo && foo[/\w+/] && foo[/\w+/].baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:361:1 + │ +361 │ foo && foo[null] && foo[null].baz; + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -358,7 +358,7 @@ +357 357 | // other literal expressions +358 358 | foo && foo[123] && foo[123].baz; +359 359 | foo && foo[true] && foo[true].baz; +360 | - foo && foo[null] && foo[null].baz; + 360 | + foo?.[null]?.baz; +361 361 | foo && foo[12n] && foo[12n].baz; +362 362 | foo && foo[/\w+/] && foo[/\w+/].baz; +363 363 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:362:1 + │ +362 │ foo && foo[12n] && foo[12n].baz; + │ ------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -359,7 +359,7 @@ +358 358 | foo && foo[123] && foo[123].baz; +359 359 | foo && foo[true] && foo[true].baz; +360 360 | foo && foo[null] && foo[null].baz; +361 | - foo && foo[12n] && foo[12n].baz; + 361 | + foo?.[12n]?.baz; +362 362 | foo && foo[/\w+/] && foo[/\w+/].baz; +363 363 | +364 364 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:363:1 + │ +363 │ foo && foo[/\w+/] && foo[/\w+/].baz; + │ ----------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -360,7 +360,7 @@ +359 359 | foo && foo[true] && foo[true].baz; +360 360 | foo && foo[null] && foo[null].baz; +361 361 | foo && foo[12n] && foo[12n].baz; +362 | - foo && foo[/\w+/] && foo[/\w+/].baz; + 362 | + foo?.[/\w+/]?.baz; +363 363 | +364 364 | +365 365 | // should preserve comments in a call expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:367:1 + │ +367 │ ┌ foo && foo.bar(/* comment */a, +368 │ │ // comment2 +369 │ │ b, ); + │ └────────' + +Suggested fix: Change to an optional chain. + | @@ -364,7 +364,7 @@ +363 363 | +364 364 | +365 365 | // should preserve comments in a call expression +366 | - foo && foo.bar(/* comment */a, + 366 | + foo?.bar(/* comment */a, +367 367 | // comment2 +368 368 | b, ); +369 369 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:372:1 + │ +372 │ foo && foo.bar != null; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -369,7 +369,7 @@ +368 368 | b, ); +369 369 | +370 370 | // ensure binary expressions that are the last expression do not get removed +371 | - foo && foo.bar != null; + 371 | + foo?.bar != null; +372 372 | foo && foo.bar != undefined; +373 373 | foo && foo.bar != null && baz; +374 374 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:373:1 + │ +373 │ foo && foo.bar != undefined; + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -370,7 +370,7 @@ +369 369 | +370 370 | // ensure binary expressions that are the last expression do not get removed +371 371 | foo && foo.bar != null; +372 | - foo && foo.bar != undefined; + 372 | + foo?.bar != undefined; +373 373 | foo && foo.bar != null && baz; +374 374 | +375 375 | // other weird cases + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:374:1 + │ +374 │ foo && foo.bar != null && baz; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -371,7 +371,7 @@ +370 370 | // ensure binary expressions that are the last expression do not get removed +371 371 | foo && foo.bar != null; +372 372 | foo && foo.bar != undefined; +373 | - foo && foo.bar != null && baz; + 373 | + foo?.bar != null && baz; +374 374 | +375 375 | // other weird cases +376 376 | foo && foo?.(); + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:377:1 + │ +377 │ foo && foo?.(); + │ -------------- + +Suggested fix: Change to an optional chain. + | @@ -374,7 +374,7 @@ +373 373 | foo && foo.bar != null && baz; +374 374 | +375 375 | // other weird cases +376 | - foo && foo?.(); + 376 | + foo?.(); +377 377 | foo.bar && foo.bar?.(); +378 378 | +379 379 | // comments + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:378:1 + │ +378 │ foo.bar && foo.bar?.(); + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -375,7 +375,7 @@ +374 374 | +375 375 | // other weird cases +376 376 | foo && foo?.(); +377 | - foo.bar && foo.bar?.(); + 377 | + foo.bar?.(); +378 378 | +379 379 | // comments +380 380 | foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:381:1 + │ +381 │ foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + │ ------------------------------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -378,7 +378,7 @@ +377 377 | foo.bar && foo.bar?.(); +378 378 | +379 379 | // comments +380 | - foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; + 380 | + foo/*1*/?./*2*/bar/*3*/?./*4*/baz/*5*/; +381 381 | foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; +382 382 | +383 383 | foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:382:1 + │ +382 │ foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + │ ------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -379,6 +379,6 @@ +378 378 | +379 379 | // comments +380 380 | foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +381 | - foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; + 381 | + foo/*1*/?.[/*2*/bar/*3*/]/*4*/?.[/*5*/baz/*6*/]/*7*/; +382 382 | +383 383 | foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ logicalAndCases.js:384:1 + │ +384 │ foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + │ --------------------------------------------------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -381,4 +381,4 @@ +380 380 | foo && foo.bar && /*0*/foo/*1*/./*2*/bar/*3*/./*4*/baz/*5*/; +381 381 | foo && foo[bar] && /*0*/foo/*1*/[/*2*/bar/*3*/]/*4*/[/*5*/baz/*6*/]/*7*/; +382 382 | +383 | - foo && foo[bar] && /*0*/foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + 383 | + foo/*1*/?./*2*/[/*3*/bar/*4*/]/*5*/?./*6*/[/*7*/baz/*8*/]/*9*/; + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts new file mode 100644 index 00000000000..560fddab7ae --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts @@ -0,0 +1,152 @@ +(foo || {}).bar; +(foo || ({})).bar; +(await foo || {}).bar; +(foo1?.foo2 || {}).foo3; +((() => foo())() || {}).bar; +const foo = (bar || {}).baz; +(foo.bar || {})[baz]; + +((foo1 || {}).foo2 || {}).foo3; +(foo || undefined || {}).bar; + +(foo() || bar || {}).baz; +((foo1 ? foo2 : foo3) || {}).foo4; + +if (foo) { (foo || {}).bar; } +if ((foo || {}).bar) { foo.bar; } + +(undefined && foo || {}).bar; +(foo ?? {}).bar; +(foo ?? ({})).bar; +(await foo ?? {}).bar; + +(foo1?.foo2 ?? {}).foo3; +((() => foo())() ?? {}).bar; +const foo = (bar ?? {}).baz; +(foo.bar ?? {})[baz]; +((foo1 ?? {}).foo2 ?? {}).foo3; + +(foo ?? undefined ?? {}).bar; +(foo() ?? bar ?? {}).baz; +((foo1 ? foo2 : foo3) ?? {}).foo4; + +if (foo) { (foo ?? {}).bar; } +if ((foo ?? {}).bar) { foo.bar; } + +(undefined && foo ?? {}).bar; +(a > b || {}).bar; +(((typeof x) as string) || {}).bar; + +(void foo() || {}).bar; +((a ? b : c) || {}).bar; + +((a instanceof Error) || {}).bar; +((a << b) || {}).bar; +((foo ** 2) || {}).bar; +(foo ** 2 || {}).bar; +(foo++ || {}).bar; +(+foo || {}).bar; + +// private name +(foo || {}).#bar; +(foo || ({})).#bar; +(await foo || {}).#bar; +(foo1?.foo2 || {}).#foo3; +((() => foo())() || {}).#bar; +const foo = (bar || {}).#baz; +(foo.bar || {})[baz]; + +((foo1 || {}).#foo2 || {}).#foo3; +(foo || undefined || {}).#bar; + +(foo() || bar || {}).#baz; +((foo1 ? foo2 : foo3) || {}).#foo4; + +if (foo) { (foo || {}).#bar; } +if ((foo || {}).#bar) { foo.bar; } + +(undefined && foo || {}).#bar; +(foo ?? {}).#bar; +(foo ?? ({})).#bar; +(await foo ?? {}).#bar; + +(foo1?.foo2 ?? {}).#foo3; +((() => foo())() ?? {}).#bar; +const foo = (bar ?? {}).#baz; +(foo.bar ?? {})[baz]; +((foo1 ?? {}).#foo2 ?? {}).#foo3; + +(foo ?? undefined ?? {}).#bar; +(foo() ?? bar ?? {}).#baz; +((foo1 ? foo2 : foo3) ?? {}).#foo4; + +if (foo) { (foo ?? {}).#bar; } +if ((foo ?? {}).#bar) { foo.bar; } + +(undefined && foo ?? {}).#bar; +(a > b || {}).#bar; +(((typeof x) as string) || {}).#bar; + +(void foo() || {}).#bar; +((a ? b : c) || {}).#bar; + +((a instanceof Error) || {}).#bar; +((a << b) || {}).#bar; +((foo ** 2) || {}).#bar; +(foo ** 2 || {}).#bar; +(foo++ || {}).#bar; +(+foo || {}).#bar; + + +//this expression +(this || {}).bar; +(this || ({})).bar; +(await this || {}).bar; +const foo = (this || {}).baz; + +((this || {}).foo2 || {}).foo3; + +if (foo) { (this || {}).bar; } +if ((this || {}).bar) { foo.bar; } + +(undefined && this || {}).bar; +(this ?? {}).bar; +(this ?? ({})).bar; +(await this ?? {}).bar; + +const foo = (this ?? {}).baz; +((this ?? {}).foo2 ?? {}).foo3; + +if (foo) { (this ?? {}).bar; } +if ((this ?? {}).bar) { foo.bar; } + +(undefined && this ?? {}).bar; +(((typeof this) as string) || {}).bar; + +// this expression with private name +(this || {}).#bar; +(this || ({})).#bar; +(await this || {}).#bar; +const foo = (this || {}).#baz; + +((this || {}).#foo2 || {}).#foo3; + +if (foo) { (this || {}).#bar; } +if ((this || {}).#bar) { foo.bar; } + +(undefined && this || {}).#bar; +(this ?? {}).#bar; +(this ?? ({})).#bar; +(await this ?? {}).#bar; + +const foo = (this ?? {}).#baz; +((this ?? {}).#foo2 ?? {}).#foo3; + +if (foo) { (this ?? {}).#bar; } +if ((this ?? {}).#bar) { foo.bar; } + +(undefined && this ?? {}).#bar; +(((typeof this) as string) || {}).#bar; +(new foo || {}).bar; +(foo() || {}).bar; +((foo || {}).bar() || {}).baz; diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts.snap b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts.snap new file mode 100644 index 00000000000..c2aa6ac5af3 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/nullishAndLogicalOr.ts.snap @@ -0,0 +1,2524 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: nullishAndLogicalOr.ts +--- +# Input +```js +(foo || {}).bar; +(foo || ({})).bar; +(await foo || {}).bar; +(foo1?.foo2 || {}).foo3; +((() => foo())() || {}).bar; +const foo = (bar || {}).baz; +(foo.bar || {})[baz]; + +((foo1 || {}).foo2 || {}).foo3; +(foo || undefined || {}).bar; + +(foo() || bar || {}).baz; +((foo1 ? foo2 : foo3) || {}).foo4; + +if (foo) { (foo || {}).bar; } +if ((foo || {}).bar) { foo.bar; } + +(undefined && foo || {}).bar; +(foo ?? {}).bar; +(foo ?? ({})).bar; +(await foo ?? {}).bar; + +(foo1?.foo2 ?? {}).foo3; +((() => foo())() ?? {}).bar; +const foo = (bar ?? {}).baz; +(foo.bar ?? {})[baz]; +((foo1 ?? {}).foo2 ?? {}).foo3; + +(foo ?? undefined ?? {}).bar; +(foo() ?? bar ?? {}).baz; +((foo1 ? foo2 : foo3) ?? {}).foo4; + +if (foo) { (foo ?? {}).bar; } +if ((foo ?? {}).bar) { foo.bar; } + +(undefined && foo ?? {}).bar; +(a > b || {}).bar; +(((typeof x) as string) || {}).bar; + +(void foo() || {}).bar; +((a ? b : c) || {}).bar; + +((a instanceof Error) || {}).bar; +((a << b) || {}).bar; +((foo ** 2) || {}).bar; +(foo ** 2 || {}).bar; +(foo++ || {}).bar; +(+foo || {}).bar; + +// private name +(foo || {}).#bar; +(foo || ({})).#bar; +(await foo || {}).#bar; +(foo1?.foo2 || {}).#foo3; +((() => foo())() || {}).#bar; +const foo = (bar || {}).#baz; +(foo.bar || {})[baz]; + +((foo1 || {}).#foo2 || {}).#foo3; +(foo || undefined || {}).#bar; + +(foo() || bar || {}).#baz; +((foo1 ? foo2 : foo3) || {}).#foo4; + +if (foo) { (foo || {}).#bar; } +if ((foo || {}).#bar) { foo.bar; } + +(undefined && foo || {}).#bar; +(foo ?? {}).#bar; +(foo ?? ({})).#bar; +(await foo ?? {}).#bar; + +(foo1?.foo2 ?? {}).#foo3; +((() => foo())() ?? {}).#bar; +const foo = (bar ?? {}).#baz; +(foo.bar ?? {})[baz]; +((foo1 ?? {}).#foo2 ?? {}).#foo3; + +(foo ?? undefined ?? {}).#bar; +(foo() ?? bar ?? {}).#baz; +((foo1 ? foo2 : foo3) ?? {}).#foo4; + +if (foo) { (foo ?? {}).#bar; } +if ((foo ?? {}).#bar) { foo.bar; } + +(undefined && foo ?? {}).#bar; +(a > b || {}).#bar; +(((typeof x) as string) || {}).#bar; + +(void foo() || {}).#bar; +((a ? b : c) || {}).#bar; + +((a instanceof Error) || {}).#bar; +((a << b) || {}).#bar; +((foo ** 2) || {}).#bar; +(foo ** 2 || {}).#bar; +(foo++ || {}).#bar; +(+foo || {}).#bar; + + +//this expression +(this || {}).bar; +(this || ({})).bar; +(await this || {}).bar; +const foo = (this || {}).baz; + +((this || {}).foo2 || {}).foo3; + +if (foo) { (this || {}).bar; } +if ((this || {}).bar) { foo.bar; } + +(undefined && this || {}).bar; +(this ?? {}).bar; +(this ?? ({})).bar; +(await this ?? {}).bar; + +const foo = (this ?? {}).baz; +((this ?? {}).foo2 ?? {}).foo3; + +if (foo) { (this ?? {}).bar; } +if ((this ?? {}).bar) { foo.bar; } + +(undefined && this ?? {}).bar; +(((typeof this) as string) || {}).bar; + +// this expression with private name +(this || {}).#bar; +(this || ({})).#bar; +(await this || {}).#bar; +const foo = (this || {}).#baz; + +((this || {}).#foo2 || {}).#foo3; + +if (foo) { (this || {}).#bar; } +if ((this || {}).#bar) { foo.bar; } + +(undefined && this || {}).#bar; +(this ?? {}).#bar; +(this ?? ({})).#bar; +(await this ?? {}).#bar; + +const foo = (this ?? {}).#baz; +((this ?? {}).#foo2 ?? {}).#foo3; + +if (foo) { (this ?? {}).#bar; } +if ((this ?? {}).#bar) { foo.bar; } + +(undefined && this ?? {}).#bar; +(((typeof this) as string) || {}).#bar; +(new foo || {}).bar; +(foo() || {}).bar; +((foo || {}).bar() || {}).baz; + +``` + +# Diagnostics +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:1:1 + │ +1 │ (foo || {}).bar; + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -1,4 +1,4 @@ +0 | - (foo || {}).bar; + 0 | + foo?.bar; +1 1 | (foo || ({})).bar; +2 2 | (await foo || {}).bar; +3 3 | (foo1?.foo2 || {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:2:1 + │ +2 │ (foo || ({})).bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -1,5 +1,5 @@ +0 0 | (foo || {}).bar; +1 | - (foo || ({})).bar; + 1 | + foo?.bar; +2 2 | (await foo || {}).bar; +3 3 | (foo1?.foo2 || {}).foo3; +4 4 | ((() => foo())() || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:3:1 + │ +3 │ (await foo || {}).bar; + │ --------------------- + +Suggested fix: Change to an optional chain. + | @@ -1,6 +1,6 @@ +0 0 | (foo || {}).bar; +1 1 | (foo || ({})).bar; +2 | - (await foo || {}).bar; + 2 | + (await foo)?.bar; +3 3 | (foo1?.foo2 || {}).foo3; +4 4 | ((() => foo())() || {}).bar; +5 5 | const foo = (bar || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:4:1 + │ +4 │ (foo1?.foo2 || {}).foo3; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -1,7 +1,7 @@ +0 0 | (foo || {}).bar; +1 1 | (foo || ({})).bar; +2 2 | (await foo || {}).bar; +3 | - (foo1?.foo2 || {}).foo3; + 3 | + foo1?.foo2?.foo3; +4 4 | ((() => foo())() || {}).bar; +5 5 | const foo = (bar || {}).baz; +6 6 | (foo.bar || {})[baz]; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:5:1 + │ +5 │ ((() => foo())() || {}).bar; + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -2,7 +2,7 @@ +1 1 | (foo || ({})).bar; +2 2 | (await foo || {}).bar; +3 3 | (foo1?.foo2 || {}).foo3; +4 | - ((() => foo())() || {}).bar; + 4 | + (() => foo())()?.bar; +5 5 | const foo = (bar || {}).baz; +6 6 | (foo.bar || {})[baz]; +7 7 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:6:13 + │ +6 │ const foo = (bar || {}).baz; + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -3,7 +3,7 @@ +2 2 | (await foo || {}).bar; +3 3 | (foo1?.foo2 || {}).foo3; +4 4 | ((() => foo())() || {}).bar; +5 | - const foo = (bar || {}).baz; + 5 | + const foo = bar?.baz; +6 6 | (foo.bar || {})[baz]; +7 7 | +8 8 | ((foo1 || {}).foo2 || {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:7:1 + │ +7 │ (foo.bar || {})[baz]; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -4,7 +4,7 @@ +3 3 | (foo1?.foo2 || {}).foo3; +4 4 | ((() => foo())() || {}).bar; +5 5 | const foo = (bar || {}).baz; +6 | - (foo.bar || {})[baz]; + 6 | + foo.bar?.[baz]; +7 7 | +8 8 | ((foo1 || {}).foo2 || {}).foo3; +9 9 | (foo || undefined || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:9:1 + │ +9 │ ((foo1 || {}).foo2 || {}).foo3; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -6,7 +6,7 @@ +5 5 | const foo = (bar || {}).baz; +6 6 | (foo.bar || {})[baz]; +7 7 | +8 | - ((foo1 || {}).foo2 || {}).foo3; + 8 | + foo1?.foo2?.foo3; +9 9 | (foo || undefined || {}).bar; +10 10 | +11 11 | (foo() || bar || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:10:1 + │ +10 │ (foo || undefined || {}).bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -7,7 +7,7 @@ + 6 6 | (foo.bar || {})[baz]; + 7 7 | + 8 8 | ((foo1 || {}).foo2 || {}).foo3; + 9 | - (foo || undefined || {}).bar; + 9 | + (foo || undefined)?.bar; +10 10 | +11 11 | (foo() || bar || {}).baz; +12 12 | ((foo1 ? foo2 : foo3) || {}).foo4; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:12:1 + │ +12 │ (foo() || bar || {}).baz; + │ ------------------------ + +Suggested fix: Change to an optional chain. + | @@ -9,7 +9,7 @@ + 8 8 | ((foo1 || {}).foo2 || {}).foo3; + 9 9 | (foo || undefined || {}).bar; +10 10 | +11 | - (foo() || bar || {}).baz; + 11 | + (foo() || bar)?.baz; +12 12 | ((foo1 ? foo2 : foo3) || {}).foo4; +13 13 | +14 14 | if (foo) { (foo || {}).bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:13:1 + │ +13 │ ((foo1 ? foo2 : foo3) || {}).foo4; + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -10,7 +10,7 @@ + 9 9 | (foo || undefined || {}).bar; +10 10 | +11 11 | (foo() || bar || {}).baz; +12 | - ((foo1 ? foo2 : foo3) || {}).foo4; + 12 | + (foo1 ? foo2 : foo3)?.foo4; +13 13 | +14 14 | if (foo) { (foo || {}).bar; } +15 15 | if ((foo || {}).bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:15:12 + │ +15 │ if (foo) { (foo || {}).bar; } + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -12,7 +12,7 @@ +11 11 | (foo() || bar || {}).baz; +12 12 | ((foo1 ? foo2 : foo3) || {}).foo4; +13 13 | +14 | - if (foo) { (foo || {}).bar; } + 14 | + if (foo) { foo?.bar; } +15 15 | if ((foo || {}).bar) { foo.bar; } +16 16 | +17 17 | (undefined && foo || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:16:5 + │ +16 │ if ((foo || {}).bar) { foo.bar; } + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -13,7 +13,7 @@ +12 12 | ((foo1 ? foo2 : foo3) || {}).foo4; +13 13 | +14 14 | if (foo) { (foo || {}).bar; } +15 | - if ((foo || {}).bar) { foo.bar; } + 15 | + if (foo?.bar) { foo.bar; } +16 16 | +17 17 | (undefined && foo || {}).bar; +18 18 | (foo ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:18:1 + │ +18 │ (undefined && foo || {}).bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -15,7 +15,7 @@ +14 14 | if (foo) { (foo || {}).bar; } +15 15 | if ((foo || {}).bar) { foo.bar; } +16 16 | +17 | - (undefined && foo || {}).bar; + 17 | + (undefined && foo)?.bar; +18 18 | (foo ?? {}).bar; +19 19 | (foo ?? ({})).bar; +20 20 | (await foo ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:19:1 + │ +19 │ (foo ?? {}).bar; + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -16,7 +16,7 @@ +15 15 | if ((foo || {}).bar) { foo.bar; } +16 16 | +17 17 | (undefined && foo || {}).bar; +18 | - (foo ?? {}).bar; + 18 | + foo?.bar; +19 19 | (foo ?? ({})).bar; +20 20 | (await foo ?? {}).bar; +21 21 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:20:1 + │ +20 │ (foo ?? ({})).bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -17,7 +17,7 @@ +16 16 | +17 17 | (undefined && foo || {}).bar; +18 18 | (foo ?? {}).bar; +19 | - (foo ?? ({})).bar; + 19 | + foo?.bar; +20 20 | (await foo ?? {}).bar; +21 21 | +22 22 | (foo1?.foo2 ?? {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:21:1 + │ +21 │ (await foo ?? {}).bar; + │ --------------------- + +Suggested fix: Change to an optional chain. + | @@ -18,7 +18,7 @@ +17 17 | (undefined && foo || {}).bar; +18 18 | (foo ?? {}).bar; +19 19 | (foo ?? ({})).bar; +20 | - (await foo ?? {}).bar; + 20 | + (await foo)?.bar; +21 21 | +22 22 | (foo1?.foo2 ?? {}).foo3; +23 23 | ((() => foo())() ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:23:1 + │ +23 │ (foo1?.foo2 ?? {}).foo3; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -20,7 +20,7 @@ +19 19 | (foo ?? ({})).bar; +20 20 | (await foo ?? {}).bar; +21 21 | +22 | - (foo1?.foo2 ?? {}).foo3; + 22 | + foo1?.foo2?.foo3; +23 23 | ((() => foo())() ?? {}).bar; +24 24 | const foo = (bar ?? {}).baz; +25 25 | (foo.bar ?? {})[baz]; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:24:1 + │ +24 │ ((() => foo())() ?? {}).bar; + │ --------------------------- + +Suggested fix: Change to an optional chain. + | @@ -21,7 +21,7 @@ +20 20 | (await foo ?? {}).bar; +21 21 | +22 22 | (foo1?.foo2 ?? {}).foo3; +23 | - ((() => foo())() ?? {}).bar; + 23 | + (() => foo())()?.bar; +24 24 | const foo = (bar ?? {}).baz; +25 25 | (foo.bar ?? {})[baz]; +26 26 | ((foo1 ?? {}).foo2 ?? {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:25:13 + │ +25 │ const foo = (bar ?? {}).baz; + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -22,7 +22,7 @@ +21 21 | +22 22 | (foo1?.foo2 ?? {}).foo3; +23 23 | ((() => foo())() ?? {}).bar; +24 | - const foo = (bar ?? {}).baz; + 24 | + const foo = bar?.baz; +25 25 | (foo.bar ?? {})[baz]; +26 26 | ((foo1 ?? {}).foo2 ?? {}).foo3; +27 27 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:26:1 + │ +26 │ (foo.bar ?? {})[baz]; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -23,7 +23,7 @@ +22 22 | (foo1?.foo2 ?? {}).foo3; +23 23 | ((() => foo())() ?? {}).bar; +24 24 | const foo = (bar ?? {}).baz; +25 | - (foo.bar ?? {})[baz]; + 25 | + foo.bar?.[baz]; +26 26 | ((foo1 ?? {}).foo2 ?? {}).foo3; +27 27 | +28 28 | (foo ?? undefined ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:27:1 + │ +27 │ ((foo1 ?? {}).foo2 ?? {}).foo3; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -24,7 +24,7 @@ +23 23 | ((() => foo())() ?? {}).bar; +24 24 | const foo = (bar ?? {}).baz; +25 25 | (foo.bar ?? {})[baz]; +26 | - ((foo1 ?? {}).foo2 ?? {}).foo3; + 26 | + foo1?.foo2?.foo3; +27 27 | +28 28 | (foo ?? undefined ?? {}).bar; +29 29 | (foo() ?? bar ?? {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:29:1 + │ +29 │ (foo ?? undefined ?? {}).bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -26,7 +26,7 @@ +25 25 | (foo.bar ?? {})[baz]; +26 26 | ((foo1 ?? {}).foo2 ?? {}).foo3; +27 27 | +28 | - (foo ?? undefined ?? {}).bar; + 28 | + (foo ?? undefined)?.bar; +29 29 | (foo() ?? bar ?? {}).baz; +30 30 | ((foo1 ? foo2 : foo3) ?? {}).foo4; +31 31 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:30:1 + │ +30 │ (foo() ?? bar ?? {}).baz; + │ ------------------------ + +Suggested fix: Change to an optional chain. + | @@ -27,7 +27,7 @@ +26 26 | ((foo1 ?? {}).foo2 ?? {}).foo3; +27 27 | +28 28 | (foo ?? undefined ?? {}).bar; +29 | - (foo() ?? bar ?? {}).baz; + 29 | + (foo() ?? bar)?.baz; +30 30 | ((foo1 ? foo2 : foo3) ?? {}).foo4; +31 31 | +32 32 | if (foo) { (foo ?? {}).bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:31:1 + │ +31 │ ((foo1 ? foo2 : foo3) ?? {}).foo4; + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -28,7 +28,7 @@ +27 27 | +28 28 | (foo ?? undefined ?? {}).bar; +29 29 | (foo() ?? bar ?? {}).baz; +30 | - ((foo1 ? foo2 : foo3) ?? {}).foo4; + 30 | + (foo1 ? foo2 : foo3)?.foo4; +31 31 | +32 32 | if (foo) { (foo ?? {}).bar; } +33 33 | if ((foo ?? {}).bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:33:12 + │ +33 │ if (foo) { (foo ?? {}).bar; } + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -30,7 +30,7 @@ +29 29 | (foo() ?? bar ?? {}).baz; +30 30 | ((foo1 ? foo2 : foo3) ?? {}).foo4; +31 31 | +32 | - if (foo) { (foo ?? {}).bar; } + 32 | + if (foo) { foo?.bar; } +33 33 | if ((foo ?? {}).bar) { foo.bar; } +34 34 | +35 35 | (undefined && foo ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:34:5 + │ +34 │ if ((foo ?? {}).bar) { foo.bar; } + │ --------------- + +Suggested fix: Change to an optional chain. + | @@ -31,7 +31,7 @@ +30 30 | ((foo1 ? foo2 : foo3) ?? {}).foo4; +31 31 | +32 32 | if (foo) { (foo ?? {}).bar; } +33 | - if ((foo ?? {}).bar) { foo.bar; } + 33 | + if (foo?.bar) { foo.bar; } +34 34 | +35 35 | (undefined && foo ?? {}).bar; +36 36 | (a > b || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:36:1 + │ +36 │ (undefined && foo ?? {}).bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -33,7 +33,7 @@ +32 32 | if (foo) { (foo ?? {}).bar; } +33 33 | if ((foo ?? {}).bar) { foo.bar; } +34 34 | +35 | - (undefined && foo ?? {}).bar; + 35 | + (undefined && foo)?.bar; +36 36 | (a > b || {}).bar; +37 37 | (((typeof x) as string) || {}).bar; +38 38 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:37:1 + │ +37 │ (a > b || {}).bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -34,7 +34,7 @@ +33 33 | if ((foo ?? {}).bar) { foo.bar; } +34 34 | +35 35 | (undefined && foo ?? {}).bar; +36 | - (a > b || {}).bar; + 36 | + (a > b)?.bar; +37 37 | (((typeof x) as string) || {}).bar; +38 38 | +39 39 | (void foo() || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:38:1 + │ +38 │ (((typeof x) as string) || {}).bar; + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -35,7 +35,7 @@ +34 34 | +35 35 | (undefined && foo ?? {}).bar; +36 36 | (a > b || {}).bar; +37 | - (((typeof x) as string) || {}).bar; + 37 | + ((typeof x) as string)?.bar; +38 38 | +39 39 | (void foo() || {}).bar; +40 40 | ((a ? b : c) || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:40:1 + │ +40 │ (void foo() || {}).bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -37,7 +37,7 @@ +36 36 | (a > b || {}).bar; +37 37 | (((typeof x) as string) || {}).bar; +38 38 | +39 | - (void foo() || {}).bar; + 39 | + (void foo())?.bar; +40 40 | ((a ? b : c) || {}).bar; +41 41 | +42 42 | ((a instanceof Error) || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:41:1 + │ +41 │ ((a ? b : c) || {}).bar; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -38,7 +38,7 @@ +37 37 | (((typeof x) as string) || {}).bar; +38 38 | +39 39 | (void foo() || {}).bar; +40 | - ((a ? b : c) || {}).bar; + 40 | + (a ? b : c)?.bar; +41 41 | +42 42 | ((a instanceof Error) || {}).bar; +43 43 | ((a << b) || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:43:1 + │ +43 │ ((a instanceof Error) || {}).bar; + │ -------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -40,7 +40,7 @@ +39 39 | (void foo() || {}).bar; +40 40 | ((a ? b : c) || {}).bar; +41 41 | +42 | - ((a instanceof Error) || {}).bar; + 42 | + (a instanceof Error)?.bar; +43 43 | ((a << b) || {}).bar; +44 44 | ((foo ** 2) || {}).bar; +45 45 | (foo ** 2 || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:44:1 + │ +44 │ ((a << b) || {}).bar; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -41,7 +41,7 @@ +40 40 | ((a ? b : c) || {}).bar; +41 41 | +42 42 | ((a instanceof Error) || {}).bar; +43 | - ((a << b) || {}).bar; + 43 | + (a << b)?.bar; +44 44 | ((foo ** 2) || {}).bar; +45 45 | (foo ** 2 || {}).bar; +46 46 | (foo++ || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:45:1 + │ +45 │ ((foo ** 2) || {}).bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -42,7 +42,7 @@ +41 41 | +42 42 | ((a instanceof Error) || {}).bar; +43 43 | ((a << b) || {}).bar; +44 | - ((foo ** 2) || {}).bar; + 44 | + (foo ** 2)?.bar; +45 45 | (foo ** 2 || {}).bar; +46 46 | (foo++ || {}).bar; +47 47 | (+foo || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:46:1 + │ +46 │ (foo ** 2 || {}).bar; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -43,7 +43,7 @@ +42 42 | ((a instanceof Error) || {}).bar; +43 43 | ((a << b) || {}).bar; +44 44 | ((foo ** 2) || {}).bar; +45 | - (foo ** 2 || {}).bar; + 45 | + (foo ** 2)?.bar; +46 46 | (foo++ || {}).bar; +47 47 | (+foo || {}).bar; +48 48 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:47:1 + │ +47 │ (foo++ || {}).bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -44,7 +44,7 @@ +43 43 | ((a << b) || {}).bar; +44 44 | ((foo ** 2) || {}).bar; +45 45 | (foo ** 2 || {}).bar; +46 | - (foo++ || {}).bar; + 46 | + (foo++)?.bar; +47 47 | (+foo || {}).bar; +48 48 | +49 49 | // private name + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:48:1 + │ +48 │ (+foo || {}).bar; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -45,7 +45,7 @@ +44 44 | ((foo ** 2) || {}).bar; +45 45 | (foo ** 2 || {}).bar; +46 46 | (foo++ || {}).bar; +47 | - (+foo || {}).bar; + 47 | + (+foo)?.bar; +48 48 | +49 49 | // private name +50 50 | (foo || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:51:1 + │ +51 │ (foo || {}).#bar; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -48,7 +48,7 @@ +47 47 | (+foo || {}).bar; +48 48 | +49 49 | // private name +50 | - (foo || {}).#bar; + 50 | + foo?.#bar; +51 51 | (foo || ({})).#bar; +52 52 | (await foo || {}).#bar; +53 53 | (foo1?.foo2 || {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:52:1 + │ +52 │ (foo || ({})).#bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -49,7 +49,7 @@ +48 48 | +49 49 | // private name +50 50 | (foo || {}).#bar; +51 | - (foo || ({})).#bar; + 51 | + foo?.#bar; +52 52 | (await foo || {}).#bar; +53 53 | (foo1?.foo2 || {}).#foo3; +54 54 | ((() => foo())() || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:53:1 + │ +53 │ (await foo || {}).#bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -50,7 +50,7 @@ +49 49 | // private name +50 50 | (foo || {}).#bar; +51 51 | (foo || ({})).#bar; +52 | - (await foo || {}).#bar; + 52 | + (await foo)?.#bar; +53 53 | (foo1?.foo2 || {}).#foo3; +54 54 | ((() => foo())() || {}).#bar; +55 55 | const foo = (bar || {}).#baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:54:1 + │ +54 │ (foo1?.foo2 || {}).#foo3; + │ ------------------------ + +Suggested fix: Change to an optional chain. + | @@ -51,7 +51,7 @@ +50 50 | (foo || {}).#bar; +51 51 | (foo || ({})).#bar; +52 52 | (await foo || {}).#bar; +53 | - (foo1?.foo2 || {}).#foo3; + 53 | + foo1?.foo2?.#foo3; +54 54 | ((() => foo())() || {}).#bar; +55 55 | const foo = (bar || {}).#baz; +56 56 | (foo.bar || {})[baz]; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:55:1 + │ +55 │ ((() => foo())() || {}).#bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -52,7 +52,7 @@ +51 51 | (foo || ({})).#bar; +52 52 | (await foo || {}).#bar; +53 53 | (foo1?.foo2 || {}).#foo3; +54 | - ((() => foo())() || {}).#bar; + 54 | + (() => foo())()?.#bar; +55 55 | const foo = (bar || {}).#baz; +56 56 | (foo.bar || {})[baz]; +57 57 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:56:13 + │ +56 │ const foo = (bar || {}).#baz; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -53,7 +53,7 @@ +52 52 | (await foo || {}).#bar; +53 53 | (foo1?.foo2 || {}).#foo3; +54 54 | ((() => foo())() || {}).#bar; +55 | - const foo = (bar || {}).#baz; + 55 | + const foo = bar?.#baz; +56 56 | (foo.bar || {})[baz]; +57 57 | +58 58 | ((foo1 || {}).#foo2 || {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:57:1 + │ +57 │ (foo.bar || {})[baz]; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -54,7 +54,7 @@ +53 53 | (foo1?.foo2 || {}).#foo3; +54 54 | ((() => foo())() || {}).#bar; +55 55 | const foo = (bar || {}).#baz; +56 | - (foo.bar || {})[baz]; + 56 | + foo.bar?.[baz]; +57 57 | +58 58 | ((foo1 || {}).#foo2 || {}).#foo3; +59 59 | (foo || undefined || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:59:1 + │ +59 │ ((foo1 || {}).#foo2 || {}).#foo3; + │ -------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -56,7 +56,7 @@ +55 55 | const foo = (bar || {}).#baz; +56 56 | (foo.bar || {})[baz]; +57 57 | +58 | - ((foo1 || {}).#foo2 || {}).#foo3; + 58 | + foo1?.#foo2?.#foo3; +59 59 | (foo || undefined || {}).#bar; +60 60 | +61 61 | (foo() || bar || {}).#baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:60:1 + │ +60 │ (foo || undefined || {}).#bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -57,7 +57,7 @@ +56 56 | (foo.bar || {})[baz]; +57 57 | +58 58 | ((foo1 || {}).#foo2 || {}).#foo3; +59 | - (foo || undefined || {}).#bar; + 59 | + (foo || undefined)?.#bar; +60 60 | +61 61 | (foo() || bar || {}).#baz; +62 62 | ((foo1 ? foo2 : foo3) || {}).#foo4; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:62:1 + │ +62 │ (foo() || bar || {}).#baz; + │ ------------------------- + +Suggested fix: Change to an optional chain. + | @@ -59,7 +59,7 @@ +58 58 | ((foo1 || {}).#foo2 || {}).#foo3; +59 59 | (foo || undefined || {}).#bar; +60 60 | +61 | - (foo() || bar || {}).#baz; + 61 | + (foo() || bar)?.#baz; +62 62 | ((foo1 ? foo2 : foo3) || {}).#foo4; +63 63 | +64 64 | if (foo) { (foo || {}).#bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:63:1 + │ +63 │ ((foo1 ? foo2 : foo3) || {}).#foo4; + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -60,7 +60,7 @@ +59 59 | (foo || undefined || {}).#bar; +60 60 | +61 61 | (foo() || bar || {}).#baz; +62 | - ((foo1 ? foo2 : foo3) || {}).#foo4; + 62 | + (foo1 ? foo2 : foo3)?.#foo4; +63 63 | +64 64 | if (foo) { (foo || {}).#bar; } +65 65 | if ((foo || {}).#bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:65:12 + │ +65 │ if (foo) { (foo || {}).#bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -62,7 +62,7 @@ +61 61 | (foo() || bar || {}).#baz; +62 62 | ((foo1 ? foo2 : foo3) || {}).#foo4; +63 63 | +64 | - if (foo) { (foo || {}).#bar; } + 64 | + if (foo) { foo?.#bar; } +65 65 | if ((foo || {}).#bar) { foo.bar; } +66 66 | +67 67 | (undefined && foo || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:66:5 + │ +66 │ if ((foo || {}).#bar) { foo.bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -63,7 +63,7 @@ +62 62 | ((foo1 ? foo2 : foo3) || {}).#foo4; +63 63 | +64 64 | if (foo) { (foo || {}).#bar; } +65 | - if ((foo || {}).#bar) { foo.bar; } + 65 | + if (foo?.#bar) { foo.bar; } +66 66 | +67 67 | (undefined && foo || {}).#bar; +68 68 | (foo ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:68:1 + │ +68 │ (undefined && foo || {}).#bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -65,7 +65,7 @@ +64 64 | if (foo) { (foo || {}).#bar; } +65 65 | if ((foo || {}).#bar) { foo.bar; } +66 66 | +67 | - (undefined && foo || {}).#bar; + 67 | + (undefined && foo)?.#bar; +68 68 | (foo ?? {}).#bar; +69 69 | (foo ?? ({})).#bar; +70 70 | (await foo ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:69:1 + │ +69 │ (foo ?? {}).#bar; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -66,7 +66,7 @@ +65 65 | if ((foo || {}).#bar) { foo.bar; } +66 66 | +67 67 | (undefined && foo || {}).#bar; +68 | - (foo ?? {}).#bar; + 68 | + foo?.#bar; +69 69 | (foo ?? ({})).#bar; +70 70 | (await foo ?? {}).#bar; +71 71 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:70:1 + │ +70 │ (foo ?? ({})).#bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -67,7 +67,7 @@ +66 66 | +67 67 | (undefined && foo || {}).#bar; +68 68 | (foo ?? {}).#bar; +69 | - (foo ?? ({})).#bar; + 69 | + foo?.#bar; +70 70 | (await foo ?? {}).#bar; +71 71 | +72 72 | (foo1?.foo2 ?? {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:71:1 + │ +71 │ (await foo ?? {}).#bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -68,7 +68,7 @@ +67 67 | (undefined && foo || {}).#bar; +68 68 | (foo ?? {}).#bar; +69 69 | (foo ?? ({})).#bar; +70 | - (await foo ?? {}).#bar; + 70 | + (await foo)?.#bar; +71 71 | +72 72 | (foo1?.foo2 ?? {}).#foo3; +73 73 | ((() => foo())() ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:73:1 + │ +73 │ (foo1?.foo2 ?? {}).#foo3; + │ ------------------------ + +Suggested fix: Change to an optional chain. + | @@ -70,7 +70,7 @@ +69 69 | (foo ?? ({})).#bar; +70 70 | (await foo ?? {}).#bar; +71 71 | +72 | - (foo1?.foo2 ?? {}).#foo3; + 72 | + foo1?.foo2?.#foo3; +73 73 | ((() => foo())() ?? {}).#bar; +74 74 | const foo = (bar ?? {}).#baz; +75 75 | (foo.bar ?? {})[baz]; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:74:1 + │ +74 │ ((() => foo())() ?? {}).#bar; + │ ---------------------------- + +Suggested fix: Change to an optional chain. + | @@ -71,7 +71,7 @@ +70 70 | (await foo ?? {}).#bar; +71 71 | +72 72 | (foo1?.foo2 ?? {}).#foo3; +73 | - ((() => foo())() ?? {}).#bar; + 73 | + (() => foo())()?.#bar; +74 74 | const foo = (bar ?? {}).#baz; +75 75 | (foo.bar ?? {})[baz]; +76 76 | ((foo1 ?? {}).#foo2 ?? {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:75:13 + │ +75 │ const foo = (bar ?? {}).#baz; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -72,7 +72,7 @@ +71 71 | +72 72 | (foo1?.foo2 ?? {}).#foo3; +73 73 | ((() => foo())() ?? {}).#bar; +74 | - const foo = (bar ?? {}).#baz; + 74 | + const foo = bar?.#baz; +75 75 | (foo.bar ?? {})[baz]; +76 76 | ((foo1 ?? {}).#foo2 ?? {}).#foo3; +77 77 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:76:1 + │ +76 │ (foo.bar ?? {})[baz]; + │ -------------------- + +Suggested fix: Change to an optional chain. + | @@ -73,7 +73,7 @@ +72 72 | (foo1?.foo2 ?? {}).#foo3; +73 73 | ((() => foo())() ?? {}).#bar; +74 74 | const foo = (bar ?? {}).#baz; +75 | - (foo.bar ?? {})[baz]; + 75 | + foo.bar?.[baz]; +76 76 | ((foo1 ?? {}).#foo2 ?? {}).#foo3; +77 77 | +78 78 | (foo ?? undefined ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:77:1 + │ +77 │ ((foo1 ?? {}).#foo2 ?? {}).#foo3; + │ -------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -74,7 +74,7 @@ +73 73 | ((() => foo())() ?? {}).#bar; +74 74 | const foo = (bar ?? {}).#baz; +75 75 | (foo.bar ?? {})[baz]; +76 | - ((foo1 ?? {}).#foo2 ?? {}).#foo3; + 76 | + foo1?.#foo2?.#foo3; +77 77 | +78 78 | (foo ?? undefined ?? {}).#bar; +79 79 | (foo() ?? bar ?? {}).#baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:79:1 + │ +79 │ (foo ?? undefined ?? {}).#bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -76,7 +76,7 @@ +75 75 | (foo.bar ?? {})[baz]; +76 76 | ((foo1 ?? {}).#foo2 ?? {}).#foo3; +77 77 | +78 | - (foo ?? undefined ?? {}).#bar; + 78 | + (foo ?? undefined)?.#bar; +79 79 | (foo() ?? bar ?? {}).#baz; +80 80 | ((foo1 ? foo2 : foo3) ?? {}).#foo4; +81 81 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:80:1 + │ +80 │ (foo() ?? bar ?? {}).#baz; + │ ------------------------- + +Suggested fix: Change to an optional chain. + | @@ -77,7 +77,7 @@ +76 76 | ((foo1 ?? {}).#foo2 ?? {}).#foo3; +77 77 | +78 78 | (foo ?? undefined ?? {}).#bar; +79 | - (foo() ?? bar ?? {}).#baz; + 79 | + (foo() ?? bar)?.#baz; +80 80 | ((foo1 ? foo2 : foo3) ?? {}).#foo4; +81 81 | +82 82 | if (foo) { (foo ?? {}).#bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:81:1 + │ +81 │ ((foo1 ? foo2 : foo3) ?? {}).#foo4; + │ ---------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -78,7 +78,7 @@ +77 77 | +78 78 | (foo ?? undefined ?? {}).#bar; +79 79 | (foo() ?? bar ?? {}).#baz; +80 | - ((foo1 ? foo2 : foo3) ?? {}).#foo4; + 80 | + (foo1 ? foo2 : foo3)?.#foo4; +81 81 | +82 82 | if (foo) { (foo ?? {}).#bar; } +83 83 | if ((foo ?? {}).#bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:83:12 + │ +83 │ if (foo) { (foo ?? {}).#bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -80,7 +80,7 @@ +79 79 | (foo() ?? bar ?? {}).#baz; +80 80 | ((foo1 ? foo2 : foo3) ?? {}).#foo4; +81 81 | +82 | - if (foo) { (foo ?? {}).#bar; } + 82 | + if (foo) { foo?.#bar; } +83 83 | if ((foo ?? {}).#bar) { foo.bar; } +84 84 | +85 85 | (undefined && foo ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:84:5 + │ +84 │ if ((foo ?? {}).#bar) { foo.bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -81,7 +81,7 @@ +80 80 | ((foo1 ? foo2 : foo3) ?? {}).#foo4; +81 81 | +82 82 | if (foo) { (foo ?? {}).#bar; } +83 | - if ((foo ?? {}).#bar) { foo.bar; } + 83 | + if (foo?.#bar) { foo.bar; } +84 84 | +85 85 | (undefined && foo ?? {}).#bar; +86 86 | (a > b || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:86:1 + │ +86 │ (undefined && foo ?? {}).#bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -83,7 +83,7 @@ +82 82 | if (foo) { (foo ?? {}).#bar; } +83 83 | if ((foo ?? {}).#bar) { foo.bar; } +84 84 | +85 | - (undefined && foo ?? {}).#bar; + 85 | + (undefined && foo)?.#bar; +86 86 | (a > b || {}).#bar; +87 87 | (((typeof x) as string) || {}).#bar; +88 88 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:87:1 + │ +87 │ (a > b || {}).#bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -84,7 +84,7 @@ +83 83 | if ((foo ?? {}).#bar) { foo.bar; } +84 84 | +85 85 | (undefined && foo ?? {}).#bar; +86 | - (a > b || {}).#bar; + 86 | + (a > b)?.#bar; +87 87 | (((typeof x) as string) || {}).#bar; +88 88 | +89 89 | (void foo() || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:88:1 + │ +88 │ (((typeof x) as string) || {}).#bar; + │ ----------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -85,7 +85,7 @@ +84 84 | +85 85 | (undefined && foo ?? {}).#bar; +86 86 | (a > b || {}).#bar; +87 | - (((typeof x) as string) || {}).#bar; + 87 | + ((typeof x) as string)?.#bar; +88 88 | +89 89 | (void foo() || {}).#bar; +90 90 | ((a ? b : c) || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:90:1 + │ +90 │ (void foo() || {}).#bar; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -87,7 +87,7 @@ +86 86 | (a > b || {}).#bar; +87 87 | (((typeof x) as string) || {}).#bar; +88 88 | +89 | - (void foo() || {}).#bar; + 89 | + (void foo())?.#bar; +90 90 | ((a ? b : c) || {}).#bar; +91 91 | +92 92 | ((a instanceof Error) || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:91:1 + │ +91 │ ((a ? b : c) || {}).#bar; + │ ------------------------ + +Suggested fix: Change to an optional chain. + | @@ -88,7 +88,7 @@ +87 87 | (((typeof x) as string) || {}).#bar; +88 88 | +89 89 | (void foo() || {}).#bar; +90 | - ((a ? b : c) || {}).#bar; + 90 | + (a ? b : c)?.#bar; +91 91 | +92 92 | ((a instanceof Error) || {}).#bar; +93 93 | ((a << b) || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:93:1 + │ +93 │ ((a instanceof Error) || {}).#bar; + │ --------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -90,7 +90,7 @@ +89 89 | (void foo() || {}).#bar; +90 90 | ((a ? b : c) || {}).#bar; +91 91 | +92 | - ((a instanceof Error) || {}).#bar; + 92 | + (a instanceof Error)?.#bar; +93 93 | ((a << b) || {}).#bar; +94 94 | ((foo ** 2) || {}).#bar; +95 95 | (foo ** 2 || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:94:1 + │ +94 │ ((a << b) || {}).#bar; + │ --------------------- + +Suggested fix: Change to an optional chain. + | @@ -91,7 +91,7 @@ +90 90 | ((a ? b : c) || {}).#bar; +91 91 | +92 92 | ((a instanceof Error) || {}).#bar; +93 | - ((a << b) || {}).#bar; + 93 | + (a << b)?.#bar; +94 94 | ((foo ** 2) || {}).#bar; +95 95 | (foo ** 2 || {}).#bar; +96 96 | (foo++ || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:95:1 + │ +95 │ ((foo ** 2) || {}).#bar; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -92,7 +92,7 @@ +91 91 | +92 92 | ((a instanceof Error) || {}).#bar; +93 93 | ((a << b) || {}).#bar; +94 | - ((foo ** 2) || {}).#bar; + 94 | + (foo ** 2)?.#bar; +95 95 | (foo ** 2 || {}).#bar; +96 96 | (foo++ || {}).#bar; +97 97 | (+foo || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:96:1 + │ +96 │ (foo ** 2 || {}).#bar; + │ --------------------- + +Suggested fix: Change to an optional chain. + | @@ -93,7 +93,7 @@ +92 92 | ((a instanceof Error) || {}).#bar; +93 93 | ((a << b) || {}).#bar; +94 94 | ((foo ** 2) || {}).#bar; +95 | - (foo ** 2 || {}).#bar; + 95 | + (foo ** 2)?.#bar; +96 96 | (foo++ || {}).#bar; +97 97 | (+foo || {}).#bar; +98 98 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:97:1 + │ +97 │ (foo++ || {}).#bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -94,7 +94,7 @@ +93 93 | ((a << b) || {}).#bar; +94 94 | ((foo ** 2) || {}).#bar; +95 95 | (foo ** 2 || {}).#bar; +96 | - (foo++ || {}).#bar; + 96 | + (foo++)?.#bar; +97 97 | (+foo || {}).#bar; +98 98 | +99 99 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:98:1 + │ +98 │ (+foo || {}).#bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -95,7 +95,7 @@ +94 94 | ((foo ** 2) || {}).#bar; +95 95 | (foo ** 2 || {}).#bar; +96 96 | (foo++ || {}).#bar; +97 | - (+foo || {}).#bar; + 97 | + (+foo)?.#bar; +98 98 | +99 99 | +100 100 | //this expression + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:102:1 + │ +102 │ (this || {}).bar; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -99,7 +99,7 @@ + 98 98 | + 99 99 | +100 100 | //this expression +101 | - (this || {}).bar; + 101 | + this?.bar; +102 102 | (this || ({})).bar; +103 103 | (await this || {}).bar; +104 104 | const foo = (this || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:103:1 + │ +103 │ (this || ({})).bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -100,7 +100,7 @@ + 99 99 | +100 100 | //this expression +101 101 | (this || {}).bar; +102 | - (this || ({})).bar; + 102 | + this?.bar; +103 103 | (await this || {}).bar; +104 104 | const foo = (this || {}).baz; +105 105 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:104:1 + │ +104 │ (await this || {}).bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -101,7 +101,7 @@ +100 100 | //this expression +101 101 | (this || {}).bar; +102 102 | (this || ({})).bar; +103 | - (await this || {}).bar; + 103 | + (await this)?.bar; +104 104 | const foo = (this || {}).baz; +105 105 | +106 106 | ((this || {}).foo2 || {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:105:13 + │ +105 │ const foo = (this || {}).baz; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -102,7 +102,7 @@ +101 101 | (this || {}).bar; +102 102 | (this || ({})).bar; +103 103 | (await this || {}).bar; +104 | - const foo = (this || {}).baz; + 104 | + const foo = this?.baz; +105 105 | +106 106 | ((this || {}).foo2 || {}).foo3; +107 107 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:107:1 + │ +107 │ ((this || {}).foo2 || {}).foo3; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -104,7 +104,7 @@ +103 103 | (await this || {}).bar; +104 104 | const foo = (this || {}).baz; +105 105 | +106 | - ((this || {}).foo2 || {}).foo3; + 106 | + this?.foo2?.foo3; +107 107 | +108 108 | if (foo) { (this || {}).bar; } +109 109 | if ((this || {}).bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:109:12 + │ +109 │ if (foo) { (this || {}).bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -106,7 +106,7 @@ +105 105 | +106 106 | ((this || {}).foo2 || {}).foo3; +107 107 | +108 | - if (foo) { (this || {}).bar; } + 108 | + if (foo) { this?.bar; } +109 109 | if ((this || {}).bar) { foo.bar; } +110 110 | +111 111 | (undefined && this || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:110:5 + │ +110 │ if ((this || {}).bar) { foo.bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -107,7 +107,7 @@ +106 106 | ((this || {}).foo2 || {}).foo3; +107 107 | +108 108 | if (foo) { (this || {}).bar; } +109 | - if ((this || {}).bar) { foo.bar; } + 109 | + if (this?.bar) { foo.bar; } +110 110 | +111 111 | (undefined && this || {}).bar; +112 112 | (this ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:112:1 + │ +112 │ (undefined && this || {}).bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -109,7 +109,7 @@ +108 108 | if (foo) { (this || {}).bar; } +109 109 | if ((this || {}).bar) { foo.bar; } +110 110 | +111 | - (undefined && this || {}).bar; + 111 | + (undefined && this)?.bar; +112 112 | (this ?? {}).bar; +113 113 | (this ?? ({})).bar; +114 114 | (await this ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:113:1 + │ +113 │ (this ?? {}).bar; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -110,7 +110,7 @@ +109 109 | if ((this || {}).bar) { foo.bar; } +110 110 | +111 111 | (undefined && this || {}).bar; +112 | - (this ?? {}).bar; + 112 | + this?.bar; +113 113 | (this ?? ({})).bar; +114 114 | (await this ?? {}).bar; +115 115 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:114:1 + │ +114 │ (this ?? ({})).bar; + │ ------------------ + +Suggested fix: Change to an optional chain. + | @@ -111,7 +111,7 @@ +110 110 | +111 111 | (undefined && this || {}).bar; +112 112 | (this ?? {}).bar; +113 | - (this ?? ({})).bar; + 113 | + this?.bar; +114 114 | (await this ?? {}).bar; +115 115 | +116 116 | const foo = (this ?? {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:115:1 + │ +115 │ (await this ?? {}).bar; + │ ---------------------- + +Suggested fix: Change to an optional chain. + | @@ -112,7 +112,7 @@ +111 111 | (undefined && this || {}).bar; +112 112 | (this ?? {}).bar; +113 113 | (this ?? ({})).bar; +114 | - (await this ?? {}).bar; + 114 | + (await this)?.bar; +115 115 | +116 116 | const foo = (this ?? {}).baz; +117 117 | ((this ?? {}).foo2 ?? {}).foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:117:13 + │ +117 │ const foo = (this ?? {}).baz; + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -114,7 +114,7 @@ +113 113 | (this ?? ({})).bar; +114 114 | (await this ?? {}).bar; +115 115 | +116 | - const foo = (this ?? {}).baz; + 116 | + const foo = this?.baz; +117 117 | ((this ?? {}).foo2 ?? {}).foo3; +118 118 | +119 119 | if (foo) { (this ?? {}).bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:118:1 + │ +118 │ ((this ?? {}).foo2 ?? {}).foo3; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -115,7 +115,7 @@ +114 114 | (await this ?? {}).bar; +115 115 | +116 116 | const foo = (this ?? {}).baz; +117 | - ((this ?? {}).foo2 ?? {}).foo3; + 117 | + this?.foo2?.foo3; +118 118 | +119 119 | if (foo) { (this ?? {}).bar; } +120 120 | if ((this ?? {}).bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:120:12 + │ +120 │ if (foo) { (this ?? {}).bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -117,7 +117,7 @@ +116 116 | const foo = (this ?? {}).baz; +117 117 | ((this ?? {}).foo2 ?? {}).foo3; +118 118 | +119 | - if (foo) { (this ?? {}).bar; } + 119 | + if (foo) { this?.bar; } +120 120 | if ((this ?? {}).bar) { foo.bar; } +121 121 | +122 122 | (undefined && this ?? {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:121:5 + │ +121 │ if ((this ?? {}).bar) { foo.bar; } + │ ---------------- + +Suggested fix: Change to an optional chain. + | @@ -118,7 +118,7 @@ +117 117 | ((this ?? {}).foo2 ?? {}).foo3; +118 118 | +119 119 | if (foo) { (this ?? {}).bar; } +120 | - if ((this ?? {}).bar) { foo.bar; } + 120 | + if (this?.bar) { foo.bar; } +121 121 | +122 122 | (undefined && this ?? {}).bar; +123 123 | (((typeof this) as string) || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:123:1 + │ +123 │ (undefined && this ?? {}).bar; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -120,7 +120,7 @@ +119 119 | if (foo) { (this ?? {}).bar; } +120 120 | if ((this ?? {}).bar) { foo.bar; } +121 121 | +122 | - (undefined && this ?? {}).bar; + 122 | + (undefined && this)?.bar; +123 123 | (((typeof this) as string) || {}).bar; +124 124 | +125 125 | // this expression with private name + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:124:1 + │ +124 │ (((typeof this) as string) || {}).bar; + │ ------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -121,7 +121,7 @@ +120 120 | if ((this ?? {}).bar) { foo.bar; } +121 121 | +122 122 | (undefined && this ?? {}).bar; +123 | - (((typeof this) as string) || {}).bar; + 123 | + ((typeof this) as string)?.bar; +124 124 | +125 125 | // this expression with private name +126 126 | (this || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:127:1 + │ +127 │ (this || {}).#bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -124,7 +124,7 @@ +123 123 | (((typeof this) as string) || {}).bar; +124 124 | +125 125 | // this expression with private name +126 | - (this || {}).#bar; + 126 | + this?.#bar; +127 127 | (this || ({})).#bar; +128 128 | (await this || {}).#bar; +129 129 | const foo = (this || {}).#baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:128:1 + │ +128 │ (this || ({})).#bar; + │ ------------------- + +Suggested fix: Change to an optional chain. + | @@ -125,7 +125,7 @@ +124 124 | +125 125 | // this expression with private name +126 126 | (this || {}).#bar; +127 | - (this || ({})).#bar; + 127 | + this?.#bar; +128 128 | (await this || {}).#bar; +129 129 | const foo = (this || {}).#baz; +130 130 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:129:1 + │ +129 │ (await this || {}).#bar; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -126,7 +126,7 @@ +125 125 | // this expression with private name +126 126 | (this || {}).#bar; +127 127 | (this || ({})).#bar; +128 | - (await this || {}).#bar; + 128 | + (await this)?.#bar; +129 129 | const foo = (this || {}).#baz; +130 130 | +131 131 | ((this || {}).#foo2 || {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:130:13 + │ +130 │ const foo = (this || {}).#baz; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -127,7 +127,7 @@ +126 126 | (this || {}).#bar; +127 127 | (this || ({})).#bar; +128 128 | (await this || {}).#bar; +129 | - const foo = (this || {}).#baz; + 129 | + const foo = this?.#baz; +130 130 | +131 131 | ((this || {}).#foo2 || {}).#foo3; +132 132 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:132:1 + │ +132 │ ((this || {}).#foo2 || {}).#foo3; + │ -------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -129,7 +129,7 @@ +128 128 | (await this || {}).#bar; +129 129 | const foo = (this || {}).#baz; +130 130 | +131 | - ((this || {}).#foo2 || {}).#foo3; + 131 | + this?.#foo2?.#foo3; +132 132 | +133 133 | if (foo) { (this || {}).#bar; } +134 134 | if ((this || {}).#bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:134:12 + │ +134 │ if (foo) { (this || {}).#bar; } + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -131,7 +131,7 @@ +130 130 | +131 131 | ((this || {}).#foo2 || {}).#foo3; +132 132 | +133 | - if (foo) { (this || {}).#bar; } + 133 | + if (foo) { this?.#bar; } +134 134 | if ((this || {}).#bar) { foo.bar; } +135 135 | +136 136 | (undefined && this || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:135:5 + │ +135 │ if ((this || {}).#bar) { foo.bar; } + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -132,7 +132,7 @@ +131 131 | ((this || {}).#foo2 || {}).#foo3; +132 132 | +133 133 | if (foo) { (this || {}).#bar; } +134 | - if ((this || {}).#bar) { foo.bar; } + 134 | + if (this?.#bar) { foo.bar; } +135 135 | +136 136 | (undefined && this || {}).#bar; +137 137 | (this ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:137:1 + │ +137 │ (undefined && this || {}).#bar; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -134,7 +134,7 @@ +133 133 | if (foo) { (this || {}).#bar; } +134 134 | if ((this || {}).#bar) { foo.bar; } +135 135 | +136 | - (undefined && this || {}).#bar; + 136 | + (undefined && this)?.#bar; +137 137 | (this ?? {}).#bar; +138 138 | (this ?? ({})).#bar; +139 139 | (await this ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:138:1 + │ +138 │ (this ?? {}).#bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -135,7 +135,7 @@ +134 134 | if ((this || {}).#bar) { foo.bar; } +135 135 | +136 136 | (undefined && this || {}).#bar; +137 | - (this ?? {}).#bar; + 137 | + this?.#bar; +138 138 | (this ?? ({})).#bar; +139 139 | (await this ?? {}).#bar; +140 140 | + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:139:1 + │ +139 │ (this ?? ({})).#bar; + │ ------------------- + +Suggested fix: Change to an optional chain. + | @@ -136,7 +136,7 @@ +135 135 | +136 136 | (undefined && this || {}).#bar; +137 137 | (this ?? {}).#bar; +138 | - (this ?? ({})).#bar; + 138 | + this?.#bar; +139 139 | (await this ?? {}).#bar; +140 140 | +141 141 | const foo = (this ?? {}).#baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:140:1 + │ +140 │ (await this ?? {}).#bar; + │ ----------------------- + +Suggested fix: Change to an optional chain. + | @@ -137,7 +137,7 @@ +136 136 | (undefined && this || {}).#bar; +137 137 | (this ?? {}).#bar; +138 138 | (this ?? ({})).#bar; +139 | - (await this ?? {}).#bar; + 139 | + (await this)?.#bar; +140 140 | +141 141 | const foo = (this ?? {}).#baz; +142 142 | ((this ?? {}).#foo2 ?? {}).#foo3; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:142:13 + │ +142 │ const foo = (this ?? {}).#baz; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -139,7 +139,7 @@ +138 138 | (this ?? ({})).#bar; +139 139 | (await this ?? {}).#bar; +140 140 | +141 | - const foo = (this ?? {}).#baz; + 141 | + const foo = this?.#baz; +142 142 | ((this ?? {}).#foo2 ?? {}).#foo3; +143 143 | +144 144 | if (foo) { (this ?? {}).#bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:143:1 + │ +143 │ ((this ?? {}).#foo2 ?? {}).#foo3; + │ -------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -140,7 +140,7 @@ +139 139 | (await this ?? {}).#bar; +140 140 | +141 141 | const foo = (this ?? {}).#baz; +142 | - ((this ?? {}).#foo2 ?? {}).#foo3; + 142 | + this?.#foo2?.#foo3; +143 143 | +144 144 | if (foo) { (this ?? {}).#bar; } +145 145 | if ((this ?? {}).#bar) { foo.bar; } + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:145:12 + │ +145 │ if (foo) { (this ?? {}).#bar; } + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -142,7 +142,7 @@ +141 141 | const foo = (this ?? {}).#baz; +142 142 | ((this ?? {}).#foo2 ?? {}).#foo3; +143 143 | +144 | - if (foo) { (this ?? {}).#bar; } + 144 | + if (foo) { this?.#bar; } +145 145 | if ((this ?? {}).#bar) { foo.bar; } +146 146 | +147 147 | (undefined && this ?? {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:146:5 + │ +146 │ if ((this ?? {}).#bar) { foo.bar; } + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -143,7 +143,7 @@ +142 142 | ((this ?? {}).#foo2 ?? {}).#foo3; +143 143 | +144 144 | if (foo) { (this ?? {}).#bar; } +145 | - if ((this ?? {}).#bar) { foo.bar; } + 145 | + if (this?.#bar) { foo.bar; } +146 146 | +147 147 | (undefined && this ?? {}).#bar; +148 148 | (((typeof this) as string) || {}).#bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:148:1 + │ +148 │ (undefined && this ?? {}).#bar; + │ ------------------------------ + +Suggested fix: Change to an optional chain. + | @@ -145,7 +145,7 @@ +144 144 | if (foo) { (this ?? {}).#bar; } +145 145 | if ((this ?? {}).#bar) { foo.bar; } +146 146 | +147 | - (undefined && this ?? {}).#bar; + 147 | + (undefined && this)?.#bar; +148 148 | (((typeof this) as string) || {}).#bar; +149 149 | (new foo || {}).bar; +150 150 | (foo() || {}).bar; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:149:1 + │ +149 │ (((typeof this) as string) || {}).#bar; + │ -------------------------------------- + +Suggested fix: Change to an optional chain. + | @@ -146,7 +146,7 @@ +145 145 | if ((this ?? {}).#bar) { foo.bar; } +146 146 | +147 147 | (undefined && this ?? {}).#bar; +148 | - (((typeof this) as string) || {}).#bar; + 148 | + ((typeof this) as string)?.#bar; +149 149 | (new foo || {}).bar; +150 150 | (foo() || {}).bar; +151 151 | ((foo || {}).bar() || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:150:1 + │ +150 │ (new foo || {}).bar; + │ ------------------- + +Suggested fix: Change to an optional chain. + | @@ -147,6 +147,6 @@ +146 146 | +147 147 | (undefined && this ?? {}).#bar; +148 148 | (((typeof this) as string) || {}).#bar; +149 | - (new foo || {}).bar; + 149 | + new foo?.bar; +150 150 | (foo() || {}).bar; +151 151 | ((foo || {}).bar() || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:151:1 + │ +151 │ (foo() || {}).bar; + │ ----------------- + +Suggested fix: Change to an optional chain. + | @@ -148,5 +148,5 @@ +147 147 | (undefined && this ?? {}).#bar; +148 148 | (((typeof this) as string) || {}).#bar; +149 149 | (new foo || {}).bar; +150 | - (foo() || {}).bar; + 150 | + foo()?.bar; +151 151 | ((foo || {}).bar() || {}).baz; + + +``` + +``` +warning[js/useOptionalChain]: Change to an optional chain. + ┌─ nullishAndLogicalOr.ts:152:1 + │ +152 │ ((foo || {}).bar() || {}).baz; + │ ----------------------------- + +Suggested fix: Change to an optional chain. + | @@ -149,4 +149,4 @@ +148 148 | (((typeof this) as string) || {}).#bar; +149 149 | (new foo || {}).bar; +150 150 | (foo() || {}).bar; +151 | - ((foo || {}).bar() || {}).baz; + 151 | + foo?.bar()?.baz; + + +``` + + diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts new file mode 100644 index 00000000000..2020bcc53d0 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts @@ -0,0 +1,38 @@ +//valid +foo || {}; +foo || ({} as any); +(foo || { bar: 1 }).bar; +(undefined && (foo || {})).bar; +foo ||= bar; +foo ||= bar || {}; +foo ||= bar?.baz; +foo ||= bar?.baz || {}; +foo ||= bar?.baz?.buzz; +(foo1 ? foo2 : foo3 || {}).foo4; +(foo = 2 || {}).bar; +func(foo || {}).bar; +foo ?? {}; +foo ||= bar ?? {}; +foo && bar; +foo && foo; +foo || bar; +foo ?? bar; +foo || foo.bar; +foo ?? foo.bar; +file !== 'index.ts' && file.endsWith('.ts'); +nextToken && sourceCode.isSpaceBetweenTokens(prevToken, nextToken); +result && this.options.shouldPreserveNodeMaps; +foo && fooBar.baz; +match && match$1 !== undefined; +foo !== null && foo !== undefined; +x['y'] !== undefined && x['y'] !== null; + +foo["some long"] && foo["some long string"].baz +foo[`some long`] && foo[`some long string`].baz +foo['some long'] && foo['some long string'].baz; +foo[123] && foo[1234].baz; +foo[true] && foo[false].baz; +foo[12n] && foo[123n].baz; +foo[/\w+/] && foo[/ab+c/].baz; + +((foo || {})()).bar; diff --git a/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts.snap b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts.snap new file mode 100644 index 00000000000..7d14dbb4d54 --- /dev/null +++ b/crates/rome_js_analyze/tests/specs/js/useOptionalChain/validCases.ts.snap @@ -0,0 +1,48 @@ +--- +source: crates/rome_js_analyze/tests/spec_tests.rs +expression: validCases.ts +--- +# Input +```js +//valid +foo || {}; +foo || ({} as any); +(foo || { bar: 1 }).bar; +(undefined && (foo || {})).bar; +foo ||= bar; +foo ||= bar || {}; +foo ||= bar?.baz; +foo ||= bar?.baz || {}; +foo ||= bar?.baz?.buzz; +(foo1 ? foo2 : foo3 || {}).foo4; +(foo = 2 || {}).bar; +func(foo || {}).bar; +foo ?? {}; +foo ||= bar ?? {}; +foo && bar; +foo && foo; +foo || bar; +foo ?? bar; +foo || foo.bar; +foo ?? foo.bar; +file !== 'index.ts' && file.endsWith('.ts'); +nextToken && sourceCode.isSpaceBetweenTokens(prevToken, nextToken); +result && this.options.shouldPreserveNodeMaps; +foo && fooBar.baz; +match && match$1 !== undefined; +foo !== null && foo !== undefined; +x['y'] !== undefined && x['y'] !== null; + +foo["some long"] && foo["some long string"].baz +foo[`some long`] && foo[`some long string`].baz +foo['some long'] && foo['some long string'].baz; +foo[123] && foo[1234].baz; +foo[true] && foo[false].baz; +foo[12n] && foo[123n].baz; +foo[/\w+/] && foo[/ab+c/].baz; + +((foo || {})()).bar; + +``` + + diff --git a/crates/rome_js_semantic/src/semantic_model.rs b/crates/rome_js_semantic/src/semantic_model.rs index d19cc34568b..466d9ef2ea0 100644 --- a/crates/rome_js_semantic/src/semantic_model.rs +++ b/crates/rome_js_semantic/src/semantic_model.rs @@ -423,7 +423,7 @@ impl FusedIterator for ScopeBindingsIter {} /// The façade for all semantic information. /// - Scope: [scope] -/// - Declrations: [declaration] +/// - Declarations: [declaration] /// /// See [SemanticModelData] for more information about the internals. #[derive(Clone)] diff --git a/crates/rome_js_syntax/src/expr_ext.rs b/crates/rome_js_syntax/src/expr_ext.rs index f8779037b5f..f5ebf54f032 100644 --- a/crates/rome_js_syntax/src/expr_ext.rs +++ b/crates/rome_js_syntax/src/expr_ext.rs @@ -3,12 +3,13 @@ use crate::numbers::parse_js_number; use crate::{ JsAnyExpression, JsAnyLiteralExpression, JsArrayExpression, JsArrayHole, JsAssignmentExpression, JsBinaryExpression, JsCallExpression, JsComputedMemberExpression, - JsLiteralMemberName, JsLogicalExpression, JsNumberLiteralExpression, JsObjectExpression, - JsReferenceIdentifier, JsRegexLiteralExpression, JsStaticMemberExpression, + JsIdentifierExpression, JsLiteralMemberName, JsLogicalExpression, JsNumberLiteralExpression, + JsObjectExpression, JsReferenceIdentifier, JsRegexLiteralExpression, JsStaticMemberExpression, JsStringLiteralExpression, JsSyntaxKind, JsSyntaxToken, JsTemplate, JsUnaryExpression, OperatorPrecedence, T, }; use crate::{JsPreUpdateExpression, JsSyntaxKind::*}; +use core::iter; use rome_rowan::{ AstNode, AstSeparatedList, NodeOrToken, SyntaxNodeText, SyntaxResult, TextRange, TextSize, }; @@ -212,6 +213,43 @@ impl JsBinaryExpression { Ok(T![>] | T![<] | T![>=] | T![<=] | T![==] | T![===] | T![!=] | T![!==]) ) } + + /// Whether this is a comparison operation similar to the optional chain + /// ```js + /// foo === undefined; + /// foo == undefined; + /// foo === null; + /// foo == null; + ///``` + pub fn is_optional_chain_like(&self) -> SyntaxResult { + if matches!( + self.operator(), + Ok(JsBinaryOperator::StrictInequality | JsBinaryOperator::Inequality) + ) { + let right = self.right()?; + + let is_right_null_expression = right + .as_js_any_literal_expression() + .map_or(false, |expression| { + expression.as_js_null_literal_expression().is_some() + }); + + if is_right_null_expression { + return Ok(true); + } + + let is_right_undefined_expression = right + .as_js_identifier_expression() + .map(|expression| expression.is_undefined()) + .transpose()? + .unwrap_or(false); + + if is_right_undefined_expression { + return Ok(true); + } + } + Ok(false) + } } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)] @@ -397,6 +435,10 @@ impl JsObjectExpression { pub fn has_trailing_comma(&self) -> bool { self.members().trailing_separator().is_some() } + + pub fn is_empty(&self) -> bool { + self.members().is_empty() + } } impl JsNumberLiteralExpression { @@ -464,6 +506,103 @@ impl JsRegexLiteralExpression { } } +impl JsAnyExpression { + /// Try to extract non `JsParenthesizedExpression` from `JsAnyExpression` + pub fn omit_parentheses(self) -> JsAnyExpression { + let first = self + .as_js_parenthesized_expression() + .and_then(|expression| expression.expression().ok()); + + iter::successors(first, |expression| { + let parenthesized = expression.as_js_parenthesized_expression()?; + parenthesized.expression().ok() + }) + .last() + .unwrap_or(self) + } + + pub fn precedence(&self) -> SyntaxResult { + let precedence = match self { + JsAnyExpression::JsSequenceExpression(_) => OperatorPrecedence::Comma, + JsAnyExpression::JsYieldExpression(_) => OperatorPrecedence::Yield, + JsAnyExpression::JsConditionalExpression(_) => OperatorPrecedence::Conditional, + JsAnyExpression::JsAssignmentExpression(_) => OperatorPrecedence::Assignment, + JsAnyExpression::JsInExpression(_) + | JsAnyExpression::JsInstanceofExpression(_) + | JsAnyExpression::TsAsExpression(_) => OperatorPrecedence::Relational, + JsAnyExpression::JsLogicalExpression(expression) => expression.operator()?.precedence(), + JsAnyExpression::JsBinaryExpression(expression) => expression.operator()?.precedence(), + JsAnyExpression::TsTypeAssertionExpression(_) + | JsAnyExpression::TsNonNullAssertionExpression(_) + | JsAnyExpression::JsUnaryExpression(_) + | JsAnyExpression::JsAwaitExpression(_) => OperatorPrecedence::Unary, + JsAnyExpression::JsPostUpdateExpression(_) + | JsAnyExpression::JsPreUpdateExpression(_) => OperatorPrecedence::Update, + JsAnyExpression::JsCallExpression(_) + | JsAnyExpression::JsNewExpression(_) + | JsAnyExpression::JsImportCallExpression(_) + | JsAnyExpression::JsSuperExpression(_) => OperatorPrecedence::LeftHandSide, + + JsAnyExpression::JsComputedMemberExpression(_) + | JsAnyExpression::JsStaticMemberExpression(_) + | JsAnyExpression::ImportMeta(_) + | JsAnyExpression::NewTarget(_) => OperatorPrecedence::Member, + + JsAnyExpression::JsThisExpression(_) + | JsAnyExpression::JsAnyLiteralExpression(_) + | JsAnyExpression::JsArrayExpression(_) + | JsAnyExpression::JsArrowFunctionExpression(_) + | JsAnyExpression::JsClassExpression(_) + | JsAnyExpression::JsFunctionExpression(_) + | JsAnyExpression::JsIdentifierExpression(_) + | JsAnyExpression::JsObjectExpression(_) + | JsAnyExpression::JsxTagExpression(_) => OperatorPrecedence::Primary, + + JsAnyExpression::JsTemplate(template) => { + if template.tag().is_some() { + OperatorPrecedence::Member + } else { + OperatorPrecedence::Primary + } + } + + JsAnyExpression::JsUnknownExpression(_) => OperatorPrecedence::lowest(), + JsAnyExpression::JsParenthesizedExpression(_) => OperatorPrecedence::highest(), + }; + + Ok(precedence) + } +} + +impl JsIdentifierExpression { + pub fn is_undefined(&self) -> SyntaxResult { + Ok(self.name()?.value_token()?.text_trimmed() == "undefined") + } +} + +impl JsAnyLiteralExpression { + pub fn value_token(&self) -> SyntaxResult { + match self { + JsAnyLiteralExpression::JsBigIntLiteralExpression(expression) => { + expression.value_token() + } + JsAnyLiteralExpression::JsBooleanLiteralExpression(expression) => { + expression.value_token() + } + JsAnyLiteralExpression::JsNullLiteralExpression(expression) => expression.value_token(), + JsAnyLiteralExpression::JsNumberLiteralExpression(expression) => { + expression.value_token() + } + JsAnyLiteralExpression::JsRegexLiteralExpression(expression) => { + expression.value_token() + } + JsAnyLiteralExpression::JsStringLiteralExpression(expression) => { + expression.value_token() + } + } + } +} + impl JsStaticMemberExpression { /// Returns `true` if this is an optional member access /// diff --git a/crates/rome_js_syntax/src/lib.rs b/crates/rome_js_syntax/src/lib.rs index 304d53ecda6..7fba560daa6 100644 --- a/crates/rome_js_syntax/src/lib.rs +++ b/crates/rome_js_syntax/src/lib.rs @@ -294,6 +294,7 @@ pub enum OperatorPrecedence { LeftHandSide = 18, Member = 19, Primary = 20, + Group = 21, } impl OperatorPrecedence { diff --git a/crates/rome_service/src/configuration/linter/rules.rs b/crates/rome_service/src/configuration/linter/rules.rs index 6a437210826..d2d57950ce5 100644 --- a/crates/rome_service/src/configuration/linter/rules.rs +++ b/crates/rome_service/src/configuration/linter/rules.rs @@ -220,7 +220,7 @@ pub struct Js { } impl Js { const CATEGORY_NAME: &'static str = "js"; - pub(crate) const CATEGORY_RULES: [&'static str; 30] = [ + pub(crate) const CATEGORY_RULES: [&'static str; 31] = [ "noArguments", "noAsyncPromiseExecutor", "noCatchAssign", @@ -245,6 +245,7 @@ impl Js { "noUnusedVariables", "useBlockStatements", "useCamelCase", + "useOptionalChain", "useSimplifiedLogicExpression", "useSingleCaseStatement", "useSingleVarDeclarator", @@ -252,7 +253,7 @@ impl Js { "useValidTypeof", "useWhile", ]; - const RECOMMENDED_RULES: [&'static str; 28] = [ + const RECOMMENDED_RULES: [&'static str; 29] = [ "noArguments", "noAsyncPromiseExecutor", "noCatchAssign", @@ -275,6 +276,7 @@ impl Js { "noUnusedTemplateLiteral", "noUnusedVariables", "useBlockStatements", + "useOptionalChain", "useSimplifiedLogicExpression", "useSingleCaseStatement", "useSingleVarDeclarator", @@ -282,7 +284,7 @@ impl Js { "useValidTypeof", "useWhile", ]; - const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 28] = [ + const RECOMMENDED_RULES_AS_FILTERS: [RuleFilter<'static>; 29] = [ RuleFilter::Rule("js", Self::CATEGORY_RULES[0]), RuleFilter::Rule("js", Self::CATEGORY_RULES[1]), RuleFilter::Rule("js", Self::CATEGORY_RULES[2]), @@ -311,6 +313,7 @@ impl Js { RuleFilter::Rule("js", Self::CATEGORY_RULES[27]), RuleFilter::Rule("js", Self::CATEGORY_RULES[28]), RuleFilter::Rule("js", Self::CATEGORY_RULES[29]), + RuleFilter::Rule("js", Self::CATEGORY_RULES[30]), ]; pub(crate) fn is_recommended(&self) -> bool { !matches!(self.recommended, Some(false)) } pub(crate) fn get_enabled_rules(&self) -> IndexSet { @@ -337,7 +340,7 @@ impl Js { pub(crate) fn is_recommended_rule(rule_name: &str) -> bool { Self::RECOMMENDED_RULES.contains(&rule_name) } - pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 28] { + pub(crate) fn recommended_rules_as_filters() -> [RuleFilter<'static>; 29] { Self::RECOMMENDED_RULES_AS_FILTERS } } diff --git a/website/src/docs/lint/rules/index.md b/website/src/docs/lint/rules/index.md index eb046091906..ed8439dc9aa 100644 --- a/website/src/docs/lint/rules/index.md +++ b/website/src/docs/lint/rules/index.md @@ -205,6 +205,14 @@ JavaScript allows the omission of curly braces when a block contains only one st Enforce camel case naming convention.
+

+ useOptionalChain (since v0.10.0) + + recommended +

+Enforce using concise optional chain instead of chained logical expressions. +
+

useSimplifiedLogicExpression (since v0.7.0) diff --git a/website/src/docs/lint/rules/useOptionalChain.md b/website/src/docs/lint/rules/useOptionalChain.md new file mode 100644 index 00000000000..60eb2470f69 --- /dev/null +++ b/website/src/docs/lint/rules/useOptionalChain.md @@ -0,0 +1,144 @@ +--- +title: Lint Rule useOptionalChain +layout: layouts/rule.liquid +--- + +# useOptionalChain (since v0.10.0) + +> This rule is recommended by Rome. + +Enforce using concise optional chain instead of chained logical expressions. + +TypeScript 3.7 added support for the optional chain operator. +This operator allows you to safely access properties and methods on objects when they are potentially `null` or `undefined`. +The optional chain operator only chains when the property value is `null` or `undefined`. +It is much safer than relying upon logical operator chaining; which chains on any truthy value. + +## Examples + +### Invalid + +```jsx +foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - foo && foo.bar && foo.bar.baz && foo.bar.baz.buzz
+  0 | + foo?.bar?.baz?.buzz
+
+
{% endraw %} + +```jsx +foo.bar && foo.bar.baz.buzz +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  foo.bar && foo.bar.baz.buzz
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - foo.bar && foo.bar.baz.buzz
+  0 | + foo.bar?.baz.buzz
+
+
{% endraw %} + +```jsx +foo !== undefined && foo.bar != undefined && foo.bar.baz !== null && foo.bar.baz.buzz +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  foo !== undefined && foo.bar != undefined && foo.bar.baz !== null && foo.bar.baz.buzz
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - foo !== undefined && foo.bar != undefined && foo.bar.baz !== null && foo.bar.baz.buzz
+  0 | + foo?.bar?.baz?.buzz
+
+
{% endraw %} + +```jsx +((foo || {}).bar || {}).baz; +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  ((foo || {}).bar || {}).baz;
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - ((foo || {}).bar || {}).baz;
+  0 | + foo?.bar?.baz;
+
+
{% endraw %} + +```jsx +(await (foo1 || {}).foo2 || {}).foo3; +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  (await (foo1 || {}).foo2 || {}).foo3;
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - (await (foo1 || {}).foo2 || {}).foo3;
+  0 | + (await foo1?.foo2)?.foo3;
+
+
{% endraw %} + +```ts +(((typeof x) as string) || {}).bar; +``` + +{% raw %}
error[js/useOptionalChain]: Change to an optional chain.
+   js/useOptionalChain.js:1:1
+  
+1  (((typeof x) as string) || {}).bar;
+   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Suggested fix: Change to an optional chain.
+    | @@ -1 +1 @@
+0   | - (((typeof x) as string) || {}).bar;
+  0 | + ((typeof x) as string)?.bar;
+
+
{% endraw %} + +### Valid + +```jsx +foo && bar; +``` + +```jsx +foo || {}; +``` + +```jsx +(foo = 2 || {}).bar; +``` + +```jsx +foo || foo.bar; +``` + +```jsx +foo["some long"] && foo["some long string"].baz +``` +