Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit ceb6f1c

Browse files
feat(eslint-plugin): [no-unnece-cond] Add allowConstantLoopConditions (typescript-eslint#1029)
Co-authored-by: Brad Zacher <[email protected]>
1 parent fa88cb9 commit ceb6f1c

File tree

3 files changed

+77
-17
lines changed

3 files changed

+77
-17
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ function head<T>(items: T[]) {
5353
}
5454
```
5555

56+
- `allowConstantLoopConditions` (default `false`) - allows constant expressions in loops.
57+
58+
Example of correct code for when `allowConstantLoopConditions` is `true`:
59+
60+
```ts
61+
while (true) {}
62+
for (; true; ) {}
63+
do {} while (true);
64+
```
65+
5666
## When Not To Use It
5767

5868
The main downside to using this rule is the need for type information.

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

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,9 @@ const isLiteral = (type: ts.Type): boolean =>
4141
isLiteralType(type);
4242
// #endregion
4343

44-
type ExpressionWithTest =
45-
| TSESTree.ConditionalExpression
46-
| TSESTree.DoWhileStatement
47-
| TSESTree.ForStatement
48-
| TSESTree.IfStatement
49-
| TSESTree.WhileStatement;
50-
5144
export type Options = [
5245
{
46+
allowConstantLoopConditions?: boolean;
5347
ignoreRhs?: boolean;
5448
},
5549
];
@@ -74,6 +68,9 @@ export default createRule<Options, MessageId>({
7468
{
7569
type: 'object',
7670
properties: {
71+
allowConstantLoopConditions: {
72+
type: 'boolean',
73+
},
7774
ignoreRhs: {
7875
type: 'boolean',
7976
},
@@ -91,10 +88,11 @@ export default createRule<Options, MessageId>({
9188
},
9289
defaultOptions: [
9390
{
91+
allowConstantLoopConditions: false,
9492
ignoreRhs: false,
9593
},
9694
],
97-
create(context, [{ ignoreRhs }]) {
95+
create(context, [{ allowConstantLoopConditions, ignoreRhs }]) {
9896
const service = getParserServices(context);
9997
const checker = service.program.getTypeChecker();
10098

@@ -165,14 +163,13 @@ export default createRule<Options, MessageId>({
165163
* Filters all LogicalExpressions to prevent some duplicate reports.
166164
*/
167165
function checkIfTestExpressionIsNecessaryConditional(
168-
node: ExpressionWithTest,
166+
node: TSESTree.ConditionalExpression | TSESTree.IfStatement,
169167
): void {
170-
if (
171-
node.test !== null &&
172-
node.test.type !== AST_NODE_TYPES.LogicalExpression
173-
) {
174-
checkNode(node.test);
168+
if (node.test.type === AST_NODE_TYPES.LogicalExpression) {
169+
return;
175170
}
171+
172+
checkNode(node.test);
176173
}
177174

178175
/**
@@ -187,14 +184,46 @@ export default createRule<Options, MessageId>({
187184
}
188185
}
189186

187+
/**
188+
* Checks that a testable expression of a loop is necessarily conditional, reports otherwise.
189+
*/
190+
function checkIfLoopIsNecessaryConditional(
191+
node:
192+
| TSESTree.DoWhileStatement
193+
| TSESTree.ForStatement
194+
| TSESTree.WhileStatement,
195+
): void {
196+
if (
197+
node.test === null ||
198+
node.test.type === AST_NODE_TYPES.LogicalExpression
199+
) {
200+
return;
201+
}
202+
203+
/**
204+
* Allow:
205+
* while (true) {}
206+
* for (;true;) {}
207+
* do {} while (true)
208+
*/
209+
if (
210+
allowConstantLoopConditions &&
211+
isBooleanLiteralType(getNodeType(node.test), true)
212+
) {
213+
return;
214+
}
215+
216+
checkNode(node.test);
217+
}
218+
190219
return {
191220
BinaryExpression: checkIfBinaryExpressionIsNecessaryConditional,
192221
ConditionalExpression: checkIfTestExpressionIsNecessaryConditional,
193-
DoWhileStatement: checkIfTestExpressionIsNecessaryConditional,
194-
ForStatement: checkIfTestExpressionIsNecessaryConditional,
222+
DoWhileStatement: checkIfLoopIsNecessaryConditional,
223+
ForStatement: checkIfLoopIsNecessaryConditional,
195224
IfStatement: checkIfTestExpressionIsNecessaryConditional,
196-
WhileStatement: checkIfTestExpressionIsNecessaryConditional,
197225
LogicalExpression: checkLogicalExpressionForUnnecessaryConditionals,
226+
WhileStatement: checkIfLoopIsNecessaryConditional,
198227
};
199228
},
200229
});

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ declare const b2: true;
9797
if(b1 && b2) {}`,
9898
options: [{ ignoreRhs: true }],
9999
},
100+
{
101+
code: `
102+
while(true) {}
103+
for (;true;) {}
104+
do {} while(true)
105+
`,
106+
options: [{ allowConstantLoopConditions: true }],
107+
},
100108
],
101109
invalid: [
102110
// Ensure that it's checking in all the right places
@@ -201,5 +209,18 @@ const t1 = (b1 && b2) ? 'yes' : 'no'`,
201209
ruleError(9, 13, 'alwaysTruthy'),
202210
],
203211
},
212+
{
213+
code: `
214+
while(true) {}
215+
for (;true;) {}
216+
do {} while(true)
217+
`,
218+
options: [{ allowConstantLoopConditions: false }],
219+
errors: [
220+
ruleError(2, 7, 'alwaysTruthy'),
221+
ruleError(3, 7, 'alwaysTruthy'),
222+
ruleError(4, 13, 'alwaysTruthy'),
223+
],
224+
},
204225
],
205226
});

0 commit comments

Comments
 (0)