8000 feat(eslint-plugin): [no-unnecessary-condition] check switch cases (#… · ronami/typescript-eslint@682299e · GitHub
[go: up one dir, main page]

Skip to content

Commit 682299e

Browse files
feat(eslint-plugin): [no-unnecessary-condition] check switch cases (typescript-eslint#9912)
* add new feature without testing it * add tests & cleanup * forgot to update the docs * refine report location * type-validate operator * WIP tests * refine tests * remove unused import * clarify docs Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com> * lint --------- Co-authored-by: Kirk Waiblinger <kirk.waiblinger@gmail.com>
1 parent 8651a07 commit 682299e

File tree

3 files changed

+44
-10
lines changed

3 files changed

+44
-10
lines changed

packages/eslint-plugin/docs/rules/no-unnecessary-condition.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The following expressions are checked:
1616

1717
- Arguments to the `&&`, `||` and `?:` (ternary) operators
1818
- Conditions for `if`, `for`, `while`, and `do-while` statements
19+
- `case`s in `switch` statements
1920
- Base values of optional chain expressions
2021

2122
## Examples

packages/eslint-plugin/src/rules/no-unnecessary-condition.ts

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -363,15 +363,18 @@ export default createRule<Options, MessageId>({
363363
'===',
364364
'!=',
365365
'!==',
366-
]);
367-
function checkIfBinaryExpressionIsNecessaryConditional(
368-
node: TSESTree.BinaryExpression,
366+
] as const);
367+
type BoolOperator = Parameters<typeof BOOL_OPERATORS.has>[0];
368+
const isBoolOperator = (operator: string): operator is BoolOperator =>
369+
(BOOL_OPERATORS as Set<string>).has(operator);
370+
function checkIfBoolExpressionIsNecessaryConditional(
371+
node: TSESTree.Node,
372+
left: TSESTree.Node,
373+
right: TSESTree.Node,
374+
operator: BoolOperator,
369375
): void {
370-
if (!BOOL_OPERATORS.has(node.operator)) {
371-
return;
372-
}
373-
const leftType = getConstrainedTypeAtLocation(services, node.left);
374-
const rightType = getConstrainedTypeAtLocation(services, node.right);
376+
const leftType = getConstrainedTypeAtLocation(services, left);
377+
const rightType = getConstrainedTypeAtLocation(services, right);
375378
if (isLiteral(leftType) && isLiteral(rightType)) {
376379
context.report({ node, messageId: 'literalBooleanExpression' });
377380
return;
@@ -390,7 +393,7 @@ export default createRule<Options, MessageId>({
390393
ts.TypeFlags.TypeVariable;
391394

392395
// Allow loose comparison to nullish values.
393-
if (node.operator === '==' || node.operator === '!=') {
396+
if (operator === '==' || operator === '!=') {
394397
flag |= NULL | UNDEFINED | VOID;
395398
}
396399

@@ -719,13 +722,34 @@ export default createRule<Options, MessageId>({
719722

720723
return {
721724
AssignmentExpression: checkAssignmentExpression,
722-
BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional,
725+
BinaryExpression(node): void {
726+
const { operator } = node;
727+
if (isBoolOperator(operator)) {
728+
checkIfBoolExpressionIsNecessaryConditional(
729+
node,
730+
node.left,
731+
node.right,
732+
operator,
733+
);
734+
}
735+
},
723736
CallExpression: checkCallExpression,
724737
ConditionalExpression: (node): void => checkNode(node.test),
725738
DoWhileStatement: checkIfLoopIsNecessaryConditional,
726739
ForStatement: checkIfLoopIsNecessaryConditional,
727740
IfStatement: (node): void => checkNode(node.test),
728741
LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals,
742+
SwitchCase({ test, parent }): void {
743+
// only check `case ...:`, not `default:`
744+
if (test) {
745+
checkIfBoolExpressionIsNecessaryConditional(
746+
test,
747+
parent.discriminant,
748+
test,
749+
'===',
750+
);
751+
}
752+
},
729753
WhileStatement: checkIfLoopIsNecessaryConditional,
730754
'MemberExpression[optional = true]': checkOptionalMemberExpression,
731755
'CallExpression[optional = true]': checkOptionalCallExpression,

packages/eslint-plugin/tests/rules/no-unnecessary-condition.test.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ for (let i = 0; b1 && b2; i++) {
7575
}
7676
const t1 = b1 && b2 ? 'yes' : 'no';
7777
for (;;) {}
78+
switch (b1) {
79+
case true:
80+
default:
81+
}
7882
`,
7983
`
8084
declare function foo(): number | void;
@@ -910,6 +914,10 @@ for (let i = 0; b1 && b2; i++) {
910914
}
911915
const t1 = b1 && b2 ? 'yes' : 'no';
912916
const t1 = b2 && b1 ? 'yes' : 'no';
917+
switch (b1) {
918+
case true:
919+
default:
920+
}
913921
`,
914922
output: null,
915923
errors: [
@@ -922,6 +930,7 @@ const t1 = b2 && b1 ? 'yes' : 'no';
922930
ruleError(12, 17, 'alwaysTruthy'),
923931
ruleError(15, 12, 'alwaysTruthy'),
924932
ruleError(16, 18, 'alwaysTruthy'),
933+
ruleError(18, 8, 'literalBooleanExpression'),
925934
],
926935
},
927936
// Ensure that it's complaining about the right things

0 commit comments

Comments
 (0)
0