Why does `if (new Boolean(false)) { ... }` run its block, and what bug does that cause in code that builds flags with `new Boolean(x)`?
answer
- conditions ask the type, not the contents
- every object is truthy, no exceptions
- the false is hidden in a slot
- == unwraps, if() does not
- use Boolean(x) or !!x
basics
~10 snew Boolean(false) is an object, not the value false, and every object is truthy — so the condition passes. The wrapped false is only visible through valueOf, which a plain if-test never consults.
solid answer
~50 s`new Boolean(false)` constructs a `Boolean` wrapper *object* that stores `false` in an internal slot. A condition converts its operand to a boolean, and that conversion maps every object to `true` without ever looking inside it — the internal `false` is simply not consulted. So the branch runs, `typeof` reports `'object'`, and `!!new Boolean(false)` is `true`. The confusing part is that other operators *do* unwrap: `new Boolean(false) == false` is `true`, because loose equality converts the object to a primitive first. That inconsistency is the whole bug — a flag built with `new Boolean` compares as false in one place and tests as true in another, and `JSON.stringify` writes it out as plain `false`, so nothing in the logs looks wrong. The fix is never to write `new Boolean`; call `Boolean(x)` or use `!!x`, both of which return a real primitive.
code
javascript · 10 linesconst flag = new Boolean(false);
console.log(typeof flag); // 'object'
console.log(flag == false); // true — == unwraps the object
console.log(flag === false); // false — no conversion under ===
console.log(Boolean(flag)); // true — every object converts to true
if (flag) console.log('branch taken'); // prints
console.log(flag.valueOf()); // false — the stored primitive
console.log(typeof Boolean(0), Boolean(0)); // 'boolean' falsego deeper
Know the one-liner: any object is truthy, and new Boolean(false) is an object. Say plainly that you convert with Boolean(x) or !!x and never with new.
Explain that boolean conversion is type-driven and never consults the wrapper's internal slot, then contrast it with the operators that do unwrap — loose ==, string interpolation, JSON.stringify — and show why that split produces the bug.
Emphasise the diagnosis problem: the value logs, serializes and loosely-compares as false while every branch treats it as true, so nothing in the output reveals it. Say how you would catch it — a typeof check at the boundary, plus a lint rule banning wrapper constructors.
Treat it as evidence for a policy: conversions at system boundaries must be total and produce primitives, and truthiness-based control flow over values of unknown provenance is a hazard worth engineering out. Be able to weigh a lint ban against runtime validation at the seams.
## Conditions convert, they do not inspect An `if` condition — like `&&`, `||`, `!`, the ternary, and a `while` test — converts its operand to a boolean. That conversion has a short, fixed table: `false`, `0`, `-0`, `0n`, `''`, `null`, `undefined` and `NaN` convert to `false`; **everything else**, including every object without exception, converts to `true`. The key word is *every*. The conversion does not look at an object's contents, does not call `valueOf`, and does not care what type of object it is. An empty array, an empty object, a function, a `Date`, and a wrapper object are all truthy on identical grounds: they are objects. ## What `new Boolean(false)` actually is `Boolean` called with `new` constructs an object whose prototype is `Boolean.prototype` and which stores the argument's boolean conversion in an internal slot. The stored value is `false`. The object holding it is still an object. ```js const flag = new Boolean(false); typeof flag // 'object' Boolean(flag) // true !!flag // true if (flag) { /* runs */ } flag.valueOf() // false — the stored primitive, only visible on demand ``` So the wrapper is simultaneously "a false" by its contents and "true" by its type, and which one you observe depends entirely on which operation you use. ## The inconsistency that makes it a real bug Operators split into two camps: **Do not unwrap** — boolean conversion (`if`, `!`, `&&`, `||`, ternary), `typeof`, `===`, `Object.is`. **Do unwrap** — loose `==`, arithmetic and relational operators, template interpolation, `String()`, `JSON.stringify`. ```js const flag = new Boolean(false); flag == false // true — == converts the object to a primitive flag === false // false — different types, no conversion if (flag) {} // runs — object → true `${flag}` // 'false' JSON.stringify(flag) // 'false' ``` Read that list again with a debugging mindset. A value that logs as `false`, serializes as `false`, and compares `== false` — and yet takes the `true` branch of every `if` in your code. There is no printout that reveals the problem; you have to reach for `typeof` or `instanceof` to see it. ## Where it appears in real code Almost never on purpose. It arrives through a few routes: someone "normalizing" a value with `new Boolean(x)` believing it is the conversion function; a generic factory that calls `new SomeType(value)` where `SomeType` happens to be `Boolean`; boxing via `Object(x)` in a generic utility; or `array.map(Object)`, which boxes every element. The old `Boolean('false')` confusion is a separate trap — the non-empty string `'false'` converts to `true` — and people sometimes reach for `new` while trying to fix it, which makes things worse rather than better. ## What to write instead To get a real boolean primitive, use the conversion form: ```js Boolean(x) // the explicit conversion !!x // the terse idiom, identical result ``` Both return a primitive `true` or `false` with `typeof === 'boolean'`, both are safe as object properties, `Set` members and `===` operands. ESLint's `no-new-wrappers` rule bans the constructor form outright, and there is no case where you should turn it off. ## Recovering from one you were handed If a boxed boolean reaches you from code you do not control, normalize it at the boundary rather than threading it inward. `Boolean(x)` will *not* help — the object is truthy, so you get `true` no matter what it wraps. You need the unwrap first: `x.valueOf()` returns the stored primitive, and `x == true` also consults it. In practice the honest fix is to make the producer stop constructing wrappers; a defensive `typeof x === 'object' ? x.valueOf() : x` at the seam is a temporary patch, not a design. ## The general shape of the lesson This is the sharpest single demonstration of a rule worth stating out loud in an interview: in JavaScript, *truthiness is a property of the type, not of the contents*. Objects are truthy because they are objects. `new Boolean(false)` is only shocking if you expected the language to look inside — and it never does.
- If a boxed boolean is handed to you, why doesn't wrapping it in `Boolean(x)` fix it?Because `Boolean(x)` performs the same conversion an `if` does, and that conversion maps every object to `true` without inspecting it — so a wrapped `false` becomes `true`. You have to unwrap first: `x.valueOf()` returns the stored primitive, and `x == true` also consults it. The real fix is upstream: stop constructing the wrapper at all.
- Are boxed numbers and strings truthy in the same way?Yes, and identically — `new Number(0)`, `new Number(NaN)` and `new String('')` are all truthy, because the boolean conversion sees an object and stops there. This is the same one-line rule applied to different wrapper types: truthiness comes from the value being an object, never from what the object contains.
- How would you spot a boxed boolean while debugging, given that it logs as false?`typeof` is the quickest discriminator — `'object'` rather than `'boolean'` — and `x instanceof Boolean` confirms it. Neither a console log, a template string, nor JSON output will show anything unusual, because all three unwrap. That asymmetry is exactly why the bug survives review: every representation you look at says false while every branch behaves as true.
saying these in an interview costs you the question
- Claiming the wrapper is falsy because it holds false
- Saying if() calls valueOf on the operand
- Thinking Boolean(x) and new Boolean(x) are equivalent
- Believing new Boolean is a safe way to normalize a flag
- Assuming a value that prints false cannot be truthy