Skip to content

Commit

Permalink
feat(eslint-plugin): added checking of loose equal ternary cases for …
Browse files Browse the repository at this point in the history
…prefer-nullish-coalescing rule
  • Loading branch information
jguddas committed May 16, 2022
1 parent 11ecbf6 commit 1a02bdc
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 3 deletions.
Expand Up @@ -71,6 +71,8 @@ Incorrect code for `ignoreTernaryTests: false`, and correct code for `ignoreTern
const foo: any = 'bar';
foo !== undefined && foo !== null ? foo : 'a string';
foo === undefined || foo === null ? 'a string' : foo;
foo == undefined ? 'a string' : foo;
foo == null ? 'a string' : foo;

const foo: ?string = 'bar';
foo !== undefined ? foo : 'a string';
Expand All @@ -87,6 +89,8 @@ Correct code for `ignoreTernaryTests: false`:
const foo: any = 'bar';
foo ?? 'a string';
foo ?? 'a string';
foo ?? 'a string';
foo ?? 'a string';

const foo: ?string = 'bar';
foo ?? 'a string';
Expand Down
46 changes: 44 additions & 2 deletions packages/eslint-plugin/src/rules/prefer-nullish-coalescing.ts
Expand Up @@ -93,10 +93,11 @@ export default util.createRule<Options, MessageIds>({
if (
node.consequent.type === AST_NODE_TYPES.Identifier &&
((node.test.type === AST_NODE_TYPES.BinaryExpression &&
node.test.operator === '!==') ||
(node.test.operator === '!==' || node.test.operator === '!=')) ||
(node.test.type === AST_NODE_TYPES.LogicalExpression &&
node.test.left.type === AST_NODE_TYPES.BinaryExpression &&
node.test.left.operator === '!=='))
(node.test.left.operator === '!==' ||
node.test.left.operator === '!=')))
) {
identifier = node.consequent;
alternate = node.alternate;
Expand All @@ -110,6 +111,11 @@ export default util.createRule<Options, MessageIds>({
}

if (
isFixableLooseTernary({
requiredOperator,
identifier,
node,
}) ||
isFixableExplicitTernary({
requiredOperator,
identifier,
Expand Down Expand Up @@ -328,6 +334,42 @@ function isFixableExplicitTernary({
return true;
}

function isFixableLooseTernary({
node,
identifier,
requiredOperator,
}: {
requiredOperator: '!==' | '===';
identifier: TSESTree.Identifier;
node: TSESTree.ConditionalExpression;
}): boolean {
if (node.test.type !== AST_NODE_TYPES.BinaryExpression) {
return false;
}

const { left, right, operator } = node.test;
if (requiredOperator === '===' && operator !== '==') {
return false;
}
if (requiredOperator === '!==' && operator !== '!=') {
return false;
}

const isIdentifier = (
i: TSESTree.Expression | TSESTree.PrivateIdentifier,
): boolean =>
i.type === AST_NODE_TYPES.Identifier && i.name === identifier.name;

if (isIdentifier(right) && (isNull(left) || isUndefined(left))) {
return true;
}

if (isIdentifier(left) && (isNull(right) || isUndefined(right))) {
return true;
}
return false;
}

/**
* This is for cases where we check either undefined or null and fall back to
* using type information to ensure that our checks are correct.
Expand Down
Expand Up @@ -86,6 +86,14 @@ x ?? 'foo';
'x === undefined || x === null ? x : y;',
'x !== undefined || x === null ? x : y;',
'x !== undefined || x === null ? y : x;',
'x == null ? x : y;',
'x == undefined ? x : y;',
'x != null ? y : x;',
'x != undefined ? y : x;',
'null == x ? x : y;',
'undefined == x ? x : y;',
'null != x ? y : x;',
'undefined != x ? y : x;',
`
declare const x: string | undefined | null;
x !== undefined ? x : y;
Expand Down Expand Up @@ -211,6 +219,14 @@ x ?? 'foo';
'null !== x && undefined !== x ? x : y;',
'undefined === x || null === x ? y : x;',
'null === x || undefined === x ? y : x;',
'undefined != x ? x : y;',
'null != x ? x : y;',
'undefined == x ? y : x;',
'null == x ? y : x;',
'x != undefined ? x : y;',
'x != null ? x : y;',
'x == undefined ? y : x;',
'x == null ? y : x;',
].map(code => ({
code,
output: null,
Expand All @@ -221,7 +237,7 @@ x ?? 'foo';
line: 1,
column: 1,
endLine: 1,
endColumn: 38,
endColumn: code.length,
suggestions: [
{
messageId: 'suggestNullish' as const,
Expand Down

0 comments on commit 1a02bdc

Please sign in to comment.