@@ -41,15 +41,9 @@ const isLiteral = (type: ts.Type): boolean =>
41
41
isLiteralType ( type ) ;
42
42
// #endregion
43
43
44
- type ExpressionWithTest =
45
- | TSESTree . ConditionalExpression
46
- | TSESTree . DoWhileStatement
47
- | TSESTree . ForStatement
48
- | TSESTree . IfStatement
49
- | TSESTree . WhileStatement ;
50
-
51
44
export type Options = [
52
45
{
46
+ allowConstantLoopConditions ?: boolean ;
53
47
ignoreRhs ?: boolean ;
54
48
} ,
55
49
] ;
@@ -74,6 +68,9 @@ export default createRule<Options, MessageId>({
74
68
{
75
69
type : 'object' ,
76
70
properties : {
71
+ allowConstantLoopConditions : {
72
+ type : 'boolean' ,
73
+ } ,
77
74
ignoreRhs : {
78
75
type : 'boolean' ,
79
76
} ,
@@ -91,10 +88,11 @@ export default createRule<Options, MessageId>({
91
88
} ,
92
89
defaultOptions : [
93
90
{
91
+ allowConstantLoopConditions : false ,
94
92
ignoreRhs : false ,
95
93
} ,
96
94
] ,
97
- create ( context , [ { ignoreRhs } ] ) {
95
+ create ( context , [ { allowConstantLoopConditions , ignoreRhs } ] ) {
98
96
const service = getParserServices ( context ) ;
99
97
const checker = service . program . getTypeChecker ( ) ;
100
98
@@ -165,14 +163,13 @@ export default createRule<Options, MessageId>({
165
163
* Filters all LogicalExpressions to prevent some duplicate reports.
166
164
*/
167
165
function checkIfTestExpressionIsNecessaryConditional (
168
- node : ExpressionWithTest ,
166
+ node : TSESTree . ConditionalExpression | TSESTree . IfStatement ,
169
167
) : 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 ;
175
170
}
171
+
172
+ checkNode ( node . test ) ;
176
173
}
177
174
178
175
/**
@@ -187,14 +184,46 @@ export default createRule<Options, MessageId>({
187
184
}
188
185
}
189
186
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
+
190
219
return {
191
220
BinaryExpression : checkIfBinaryExpressionIsNecessaryConditional ,
192
221
ConditionalExpression : checkIfTestExpressionIsNecessaryConditional ,
193
- DoWhileStatement : checkIfTestExpressionIsNecessaryConditional ,
194
- ForStatement : checkIfTestExpressionIsNecessaryConditional ,
222
+ DoWhileStatement : checkIfLoopIsNecessaryConditional ,
223
+ ForStatement : checkIfLoopIsNecessaryConditional ,
195
224
IfStatement : checkIfTestExpressionIsNecessaryConditional ,
196
- WhileStatement : checkIfTestExpressionIsNecessaryConditional ,
197
225
LogicalExpression : checkLogicalExpressionForUnnecessaryConditionals ,
226
+ WhileStatement : checkIfLoopIsNecessaryConditional ,
198
227
} ;
199
228
} ,
200
229
} ) ;
0 commit comments