Skip to content

Commit f9a9fbf

Browse files
dimaborybradzacher
authored andcommitted
fix(eslint-plugin): [prefer-null-coal] fixer w/ mixed logicals (#1326)
1 parent 3923a09 commit f9a9fbf

File tree

3 files changed

+59
-12
lines changed

3 files changed

+59
-12
lines changed

packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts

+20-8
Original file line numberDiff line numberDiff line change
@@ -103,24 +103,36 @@ export default util.createRule<Options, MessageIds>({
103103
util.NullThrowsReasons.MissingToken('operator', node.type),
104104
);
105105

106+
function* fix(
107+
fixer: TSESLint.RuleFixer,
108+
): IterableIterator<TSESLint.RuleFix> {
109+
if (node.parent && util.isLogicalOrOperator(node.parent)) {
110+
// '&&' and '??' operations cannot be mixed without parentheses (e.g. a && b ?? c)
111+
if (
112+
node.left.type === AST_NODE_TYPES.LogicalExpression &&
113+
!util.isLogicalOrOperator(node.left.left)
114+
) {
115+
yield fixer.insertTextBefore(node.left.right, '(');
116+
} else {
117+
yield fixer.insertTextBefore(node.left, '(');
118+
}
119+
yield fixer.insertTextAfter(node.right, ')');
120+
}
121+
yield fixer.replaceText(barBarOperator, '??');
122+
}
123+
106124
const fixer =
107125
isMixedLogical || forceSuggestionFixer
108126
? // suggestion instead for cases where we aren't sure if the fixer is completely safe
109127
({
110128
suggest: [
111129
{
112130
messageId: 'preferNullish',
113-
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
114-
return fixer.replaceText(barBarOperator, '??');
115-
},
131+
fix,
116132
},
117133
],
118134
} as const)
119-
: {
120-
fix(fixer: TSESLint.RuleFixer): TSESLint.RuleFix {
121-
return fixer.replaceText(barBarOperator, '??');
122-
},
123-
};
135+
: { fix };
124136

125137
context.report({
126138
node: barBarOperator,

packages/eslint-plugin/src/util/astUtils.ts

+12-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
2-
TSESTree,
3-
AST_TOKEN_TYPES,
42
AST_NODE_TYPES,
3+
AST_TOKEN_TYPES,
4+
TSESTree,
55
} from '@typescript-eslint/experimental-utils';
66

77
const LINEBREAK_MATCHER = /\r\n|[\r\n\u2028\u2029]/;
@@ -42,6 +42,15 @@ function isOptionalOptionalChain(
4242
);
4343
}
4444

45+
/**
46+
* Returns true if and only if the node represents logical OR
47+
*/
48+
function isLogicalOrOperator(node: TSESTree.Node): boolean {
49+
return (
50+
node.type === AST_NODE_TYPES.LogicalExpression && node.operator === '||'
51+
);
52+
}
53+
4554
/**
4655
* Determines whether two adjacent tokens are on the same line
4756
*/
@@ -59,5 +68,6 @@ export {
5968
isOptionalChainPunctuator,
6069
isOptionalOptionalChain,
6170
isTokenOnSameLine,
71+
isLogicalOrOperator,
6272
LINEBREAK_MATCHER,
6373
};

packages/eslint-plugin/tests/rules/prefer-nullish-coalescing.test.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ declare const a: ${type} | ${nullish};
317317
declare const b: ${type} | ${nullish};
318318
declare const c: ${type} | ${nullish};
319319
declare const d: ${type} | ${nullish};
320-
a ?? b || c && d;
320+
(a ?? b) || c && d;
321321
`.trimRight(),
322322
},
323323
],
@@ -367,7 +367,7 @@ declare const a: ${type} | ${nullish};
367367
declare const b: ${type} | ${nullish};
368368
declare const c: ${type} | ${nullish};
369369
declare const d: ${type} | ${nullish};
370-
a && b ?? c || d;
370+
a && (b ?? c) || d;
371371
`.trimRight(),
372372
},
373373
],
@@ -463,5 +463,30 @@ x ?? 'foo';
463463
},
464464
],
465465
},
466+
467+
// https://github.com/typescript-eslint/typescript-eslint/issues/1290
468+
...nullishTypeInvalidTest((nullish, type) => ({
469+
code: `
470+
declare const a: ${type} | ${nullish};
471+
declare const b: ${type};
472+
declare const c: ${type};
473+
a || b || c;
474+
`.trimRight(),
475+
output: `
476+
declare const a: ${type} | ${nullish};
477+
declare const b: ${type};
478+
declare const c: ${type};
479+
(a ?? b) || c;
480+
`.trimRight(),
481+
errors: [
482+
{
483+
messageId: 'preferNullish',
484+
line: 5,
485+
column: 3,
486+
endLine: 5,
487+
endColumn: 5,
488+
},
489+
],
490+
})),
466491
],
467492
});

0 commit comments

Comments
 (0)