eslint no-extra-boolean-cast
labelled safe
Analysis Nemotron 3 Super
The rule flags unnecessary Boolean casts (!! or Boolean()) in contexts where values are automatically coerced to Boolean.
Fix: Removes the double negation (!!) or Boolean() call, keeping only the inner expression.
8 hazards
- Rewriting !!foo to foo in a comma operator like (console.log(i), !!i < 10) changes evaluation order if foo has side effects, as the inner expression may no longer be evaluated.
- Replacing Boolean(!!bar) with !!bar in a context like new Boolean(!!bar) alters behavior because new Boolean() creates an object wrapper, while !!bar is a primitive boolean.
- Removing !! from !!foo in foo && !!bar ? baz : bat could change type coercion in the ternary branches if !!bar was ensuring a boolean for consistent comparison.
- Stripping Boolean() from Boolean(bar ?? baz) in a logical expression may alter truthiness evaluation if bar or baz are objects with custom valueOf/toString methods that affect coercion differently than direct boolean conversion.
- Deleting !! from !!expr in a return statement like return !!expr; is safe only if the function expects a boolean; removing it could return a truthy/falsy value instead of strict true/false, breaking strict equality checks elsewhere.
- Removing !! from !!foo in a loop condition like for (; !!foo; ) may cause infinite loops if foo is an object that becomes false only after double negation due to [[ToBoolean]] quirks (though rare, theoretically possible with exotic objects).
- Eliminating Boolean() from Boolean((bar, baz, !!bat)) in a comma operator could drop the side effect of evaluating !!bat if bat is undefined and the rewrite leads to parsing errors or unexpected values.
- Applying the fix inside nested expressions like if ((!!foo || bar) && !!baz) when enforceForInnerExpressions is true may remove necessary casts if the outer context doesn't coerce to boolean, but the inner !! was for value consistency, not just final coercion.
Programs
| id | result | title | hazard |
|---|---|---|---|
| r1-p001 | silent prediction off | Comment attachment during removal | comments attached to the removed or moved code |
| r1-p002 | silent prediction off | Temporal Dead Zone via hoisting interaction | temporal dead zone after code is hoisted or reordered |
| r1-p003 | fix ok | Sparse array indexing | sparse arrays, holes, Array(n) vs [n], array-likes |
| r1-p004 | silent prediction off | __proto__ key preservation | __proto__ keys, prototype access, Object.assign vs spread |
| r1-p005 | fix ok | Coercion side effects in valueOf/toString | numeric/string coercion: + vs template literal, objects with toString/valueOf, Symbol, BigInt |
| r1-p006 | silent prediction off | null vs undefined short-circuit | null/undefined distinction, optional chaining short-circuit, nullish vs falsy |
| r1-p007 | silent prediction off | Comma operator side effect loss | Eliminating Boolean() from Boolean((bar, baz, !!bat)) in a comma operator could drop the side effect of evaluating !!bat if bat is undefined and the rewrite leads to parsing errors or unexpected value |
| r1-p008 | fix ok | Label and break interaction | label/break/continue control flow, finally blocks, early returns in nested blocks |
| r2-p001 | silent prediction off | Comma Operator Side Effect Loss | Rewriting !!foo to foo in a comma operator like (console.log(i), !!i < 10) changes evaluation order if foo has side effects, as the inner expression may no longer be evaluated. |
| r2-p002 | silent prediction off | Return Statement Boolean Cast | Deleting !! from !!expr in a return statement like return !!expr; is safe only if the function expects a boolean; removing it could return a truthy/falsy value instead of strict true/false, breaking s |
| r2-p003 | silent prediction off | Regular Expressions Unicode and lastIndex | regular expressions: escapes, unicode flag, lastIndex with the g flag |
| r2-p004 | silent prediction off | Strict vs Sloppy Mode Semantics | strict vs sloppy mode semantics |
| r2-p005 | fix ok | Sparse Arrays Holes Array(n) | sparse arrays, holes, Array(n) vs [n], array-likes |
| r2-p006 | silent prediction off | Comma Operator Bat Undefined Side Effect | Eliminating Boolean() from Boolean((bar, baz, !!bat)) in a comma operator could drop the side effect of evaluating !!bat if bat is undefined and the rewrite leads to parsing errors or unexpected value |
| r2-p007 | silent prediction off | This Binding Arrow Function | this binding: arrow vs function, methods extracted from objects, call/apply/bind |
| r2-p008 | silent prediction off | Exceptions Inside Rewritten Code | exceptions thrown inside the code the fix removes or rewrites |
Sources
r1-p001 · Comment attachment during removal
1// This comment should stay
2let x = !!/* keep-me */false;
3console.log(x);
false
r1-p002 · Temporal Dead Zone via hoisting interaction
1function f() {
2 console.log(typeof y);
3 let y = !!false;
4}
5f();
r1-p003 · Sparse array indexing
1const arr = Array(3);
2arr[1] = false;
3if (!!arr[0]) console.log('truthy'); else console.log('falsy'); // hole -> undefined
4console.log(arr.length);
--- orig.js 2026-10-22 14:16:53.003671515 -0400 +++ fixed.js 2026-10-22 14:16:56.454784753 -0400 @@ -1,4 +1,4 @@ const arr = Array(3); arr[1] = false; -if (!!arr[0]) console.log('truthy'); else console.log('falsy'); // hole -> undefined +if (arr[0]) console.log('truthy'); else console.log('falsy'); // hole -> undefined console.log(arr.length);
falsy 3
r1-p004 · __proto__ key preservation
1const obj = { __proto__: null };
2obj.test = !!false;
3console.log(Object.hasOwn(obj, 'test'));
4console.log(obj.test);
true false
r1-p005 · Coercion side effects in valueOf/toString
1const obj = {
2 valueOf() { return 0; }
3};
4if (!!obj) console.log('truthy'); else console.log('falsy');
--- orig.js 2026-10-22 14:16:53.004671548 -0400 +++ fixed.js 2026-10-22 14:16:55.359748756 -0400 @@ -1,4 +1,4 @@ const obj = { valueOf() { return 0; } }; -if (!!obj) console.log('truthy'); else console.log('falsy'); +if (obj) console.log('truthy'); else console.log('falsy');
truthy
r1-p006 · null vs undefined short-circuit
1let v = undefined;
2console.log(!!v ?? 'default');
3v = null;
4console.log(!!v ?? 'default');
false false
r1-p007 · Comma operator side effect loss
1let bat;
2let result = Boolean((console.log('side'), 1, !!bat));
3console.log(result); // Should log false and execute side effect
side false
r1-p008 · Label and break interaction
1outer: {
2 if (!!false) break outer;
3 console.log('not reached');
4}
5console.log('done');
--- orig.js 2026-10-22 14:16:55.659758611 -0400 +++ fixed.js 2026-10-22 14:16:58.601855516 -0400 @@ -1,5 +1,5 @@ outer: { - if (!!false) break outer; + if (false) break outer; console.log('not reached'); } console.log('done');
not reached done
r2-p001 · Comma Operator Side Effect Loss
1let i = 0;
2function logAndInc() {
3 console.log('side', i);
4 return i++;
5}
6if ((logAndInc(), !!logAndInc()) < 10) {
7 console.log('pass');
8} else {
9 console.log('fail', i);
10}
side 0 side 1 pass
r2-p002 · Return Statement Boolean Cast
1function getFlag() { return 1; }
2function check() { return !!getFlag(); }
3const r = check();
4console.log(typeof r, r === true ? 'strict true' : 'not strict');
boolean strict true
r2-p003 · Regular Expressions Unicode and lastIndex
1const re = /\u{61}/gu;
2re.lastIndex = 0;
3const str = 'a';
4const match = re.exec(str);
5console.log(match ? !!match[0] : null, match ? match.index : -1);
true 0
r2-p004 · Strict vs Sloppy Mode Semantics
1"use strict";
2function f() {
3 return !!arguments.callee;
4}
5try {
6 const r = f();
7 console.log(r);
8} catch (e) {
9 console.log(e.name, e.message);
10}
TypeError 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
r2-p005 · Sparse Arrays Holes Array(n)
1const arr = Array(3);
2arr[1] = 0;
3let sum = 0;
4for (let i = 0; i < arr.length; i++) {
5 if (!!arr[i]) sum += arr[i];
6}
7console.log(sum);
--- orig.js 2026-10-22 14:17:05.517085054 -0400 +++ fixed.js 2026-10-22 14:17:08.429182447 -0400 @@ -2,6 +2,6 @@ arr[1] = 0; let sum = 0; for (let i = 0; i < arr.length; i++) { - if (!!arr[i]) sum += arr[i]; + if (arr[i]) sum += arr[i]; } console.log(sum);
0
r2-p006 · Comma Operator Bat Undefined Side Effect
1let bat;
2function log() { console.log('bat eval'); }
3const val = (() => {
4 const bar = 1, baz = 2;
5 return Boolean((bar, baz, !!bat));
6})();
7console.log(val);
false
r2-p007 · This Binding Arrow Function
1const obj = {
2 value: 42,
3 getValue: function() { return !!this.value; }
4};
5const fn = obj.getValue;
6console.log(fn());
false
r2-p008 · Exceptions Inside Rewritten Code
1function risky() {
2 throw new Error('boom');
3}
4try {
5 const result = !!risky();
6 console.log(result);
7} catch (e) {
8 console.log(e.name, e.message);
9}
Error boom