Why Does [] == ![] Evaluate to True? Unraveling JavaScript Type Conversion
This article explains JavaScript’s explicit and implicit type conversion mechanisms—including Number(), String(), Boolean(), toString and valueOf—through detailed code examples and clarifies puzzling expressions like [] == ![] and {} == !{}.
Forced Type Conversion
Number()
Primitive arguments
<code>Number(123) // 123
Number('123') // 123
Number('123abc') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0</code>Number() is stricter than
parseInt. When the string cannot be fully parsed, it returns
NaN:
<code>parseInt('32 abc') // 32
parseInt('0x11 abc') // 17
Number('32 abc') // NaN
Number('0x11 abc') // NaN</code>Object arguments
<code>Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5</code>When an object provides both
valueOfand
toString,
Number()follows a specific order:
<code>const obj = {
toString() { return 1; },
valueOf() { return 2; }
};
console.log(Number(obj)); // 2
</code>If
valueOfreturns an object,
toStringis used instead:
<code>const obj = {
toString() { return 1; },
valueOf() { return {}; }
};
console.log(Number(obj)); // 1
</code>When both methods return objects, a
TypeError: Cannot convert object to primitive valueis thrown.
<code>const obj = {
valueOf() { return {}; }
};
console.log(Number(obj)); // NaN (no error because only valueOf is present)
</code>For an empty object, the default
Object.prototype.toStringis invoked:
<code>// Number({})
Object.prototype.toString.call({}) // "[object Object]"
Number("[object Object]") // NaN
</code>For an empty array,
Array.prototype.toStringyields an empty string, which converts to
0:
<code>// Number([])
Array.prototype.toString.call([]) // ""
Number('') // 0
</code>String()
Primitive arguments
<code>String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"
</code>Object arguments
<code>String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"
</code>When an object defines both
toStringand
valueOf,
String()prefers
toStringfirst:
<code>const obj = {
toString() { return 1; },
valueOf() { return 2; }
};
console.log(String(obj)); // "1"
</code>Boolean()
Falsy values ("falsey")
<code>undefined
null
0 // +0 and -0
NaN
false
''
</code>All other values are truthy:
<code>Boolean([]) // true
Boolean({}) // true
Boolean(new Error()) // true
Boolean(Symbol()) // true
</code>Implicit (Automatic) Type Conversion
Automatic conversion to Boolean
Control‑flow keywords and logical operators coerce their operands to Boolean.
Automatic conversion to String
When the
+operator involves a string, the other operand is converted to a string.
<code>'5' + 1 // "51"
'5' + true // "5true"
'5' + {} // "5[object Object]"
'5' + [] // "5"
</code>Automatic conversion to Number
All arithmetic operators except
+(when used for concatenation) coerce operands to numbers.
<code>'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
null + 1 // 1
undefined + 1 // NaN
</code>Relational operators (
==,
!=,
===, etc.) also perform numeric conversion when needed.
<code>1 == true // true
'1' == true // true
'1' == 1 // true
[1] == 1 // true
[] == false // true
[] == null // false
</code>Special Values
Infinity
<code>Number(Infinity) // Infinity
1 / 0 // Infinity
1 / Infinity // 0
Infinity === Infinity // true
Infinity === -Infinity // false
0 === -0 // true
0 / 0 // NaN
</code>NaN
<code>NaN == NaN // false
</code>undefined and null
<code>null === null // true
undefined === undefined // true
undefined == null // true
undefined === null // false
</code>Summary
Now we answer the initial puzzles.
[] == ![]evaluates to
truebecause
[]converts to an empty string
'', which becomes
0;
![]is
false, which also becomes
0, so
0 == 0is
true.
<code>// [] == ![]
[].toString() // ''
Number('') // 0
![] // false
Number(false) // 0
0 == 0 // true
</code> {} == !{}evaluates to
falsebecause
{}converts to
"[object Object]", which becomes
NaN;
!{}is
false(=> 0), and
NaN != 0.
<code>// {} == !{}
({}).toString() // "[object Object]"
Number('[object Object]') // NaN
!{} // false
Number(false) // 0
NaN != 0 // false overall
</code>Further examples such as
{} + {},
[] + {}, and
{} + []produce string concatenations according to the same conversion rules; you can experiment at jsisweird.com .
KooFE Frontend Team
Follow the latest frontend updates
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.