diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts index ec1a811ac3c..5ebb8e02ae5 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts @@ -485,23 +485,26 @@ export function analyzeChain( } })(); - let subChain: ValidOperand[] = []; + // Things like x !== null && x !== undefined have two nodes, but they are + // one logical unit here, so we'll allow them to be grouped. + let subChain: (ValidOperand | readonly ValidOperand[])[] = []; const maybeReportThenReset = ( - newChainSeed?: readonly ValidOperand[], + newChainSeed?: readonly [ValidOperand, ...ValidOperand[]], ): void => { if (subChain.length > 1) { + const subChainFlat = subChain.flat(); context.report({ messageId: 'preferOptionalChain', loc: { - start: subChain[0].node.loc.start, - end: subChain[subChain.length - 1].node.loc.end, + start: subChainFlat[0].node.loc.start, + end: subChainFlat[subChainFlat.length - 1].node.loc.end, }, ...getFixer( context.sourceCode, parserServices, operator, options, - subChain, + subChainFlat, ), }); } @@ -518,13 +521,11 @@ export function analyzeChain( // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ first "chain" // ^^^^^^^^^^^ newChainSeed // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ second chain - subChain = newChainSeed ? [...newChainSeed] : []; + subChain = newChainSeed ? [newChainSeed] : []; }; for (let i = 0; i < chain.length; i += 1) { - const lastOperand = subChain[subChain.length - 1] as - | ValidOperand - | undefined; + const lastOperand = subChain.flat().at(-1); const operand = chain[i]; const validatedOperands = analyzeOperand(parserServices, operand, i, chain); diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts index cee379d45ac..b0d1ffcf201 100644 --- a/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain/prefer-optional-chain.test.ts @@ -28,6 +28,35 @@ describe('|| {}', () => { 'foo ?? {};', '(foo ?? {})?.bar;', 'foo ||= bar ?? {};', + // https://github.com/typescript-eslint/typescript-eslint/issues/8380 + ` + const a = null; + const b = 0; + a === undefined || b === null || b === undefined; + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/8380 + ` + const a = 0; + const b = 0; + a === undefined || b === undefined || b === null; + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/8380 + ` + const a = 0; + const b = 0; + b === null || a === undefined || b === undefined; + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/8380 + ` + const b = 0; + b === null || b === undefined; + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/8380 + ` + const a = 0; + const b = 0; + b != null && a !== null && a !== undefined; + `, ], invalid: [ {