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 {} == !{}.

KooFE Frontend Team
KooFE Frontend Team
KooFE Frontend Team
Why Does [] == ![] Evaluate to True? Unraveling JavaScript Type Conversion

Forced Type Conversion

Number()

Primitive arguments

Number(123) // 123
Number('123') // 123
Number('123abc') // NaN
Number('') // 0
Number(true) // 1
Number(false) // 0
Number(undefined) // NaN
Number(null) // 0

Number() is stricter than parseInt. When the string cannot be fully parsed, it returns NaN:

parseInt('32 abc') // 32
parseInt('0x11 abc') // 17
Number('32 abc') // NaN
Number('0x11 abc') // NaN

Object arguments

Number({a: 1}) // NaN
Number([1, 2, 3]) // NaN
Number([5]) // 5

When an object provides both valueOf and toString, Number() follows a specific order:

const obj = {
  toString() { return 1; },
  valueOf() { return 2; }
};
console.log(Number(obj)); // 2

If valueOf returns an object, toString is used instead:

const obj = {
  toString() { return 1; },
  valueOf() { return {}; }
};
console.log(Number(obj)); // 1

When both methods return objects, a TypeError: Cannot convert object to primitive value is thrown.

const obj = {
  valueOf() { return {}; }
};
console.log(Number(obj)); // NaN (no error because only valueOf is present)

For an empty object, the default Object.prototype.toString is invoked:

// Number({})
Object.prototype.toString.call({}) // "[object Object]"
Number("[object Object]") // NaN

For an empty array, Array.prototype.toString yields an empty string, which converts to 0:

// Number([])
Array.prototype.toString.call([]) // ""
Number('') // 0

String()

Primitive arguments

String(123) // "123"
String('abc') // "abc"
String(true) // "true"
String(undefined) // "undefined"
String(null) // "null"

Object arguments

String({a: 1}) // "[object Object]"
String([1, 2, 3]) // "1,2,3"

When an object defines both toString and valueOf, String() prefers toString first:

const obj = {
  toString() { return 1; },
  valueOf() { return 2; }
};
console.log(String(obj)); // "1"

Boolean()

Falsy values ("falsey")

undefined
null
0 // +0 and -0
NaN
false
''

All other values are truthy:

Boolean([]) // true
Boolean({}) // true
Boolean(new Error()) // true
Boolean(Symbol()) // true

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.

'5' + 1 // "51"
'5' + true // "5true"
'5' + {} // "5[object Object]"
'5' + [] // "5"

Automatic conversion to Number

All arithmetic operators except + (when used for concatenation) coerce operands to numbers.

'5' - '2' // 3
'5' * '2' // 10
true - 1 // 0
false - 1 // -1
null + 1 // 1
undefined + 1 // NaN

Relational operators ( ==, !=, ===, etc.) also perform numeric conversion when needed.

1 == true // true
'1' == true // true
'1' == 1 // true
[1] == 1 // true
[] == false // true
[] == null // false

Special Values

Infinity

Number(Infinity) // Infinity
1 / 0 // Infinity
1 / Infinity // 0
Infinity === Infinity // true
Infinity === -Infinity // false
0 === -0 // true
0 / 0 // NaN

NaN

NaN == NaN // false

undefined and null

null === null // true
undefined === undefined // true
undefined == null // true
undefined === null // false

Summary

Now we answer the initial puzzles. [] == ![] evaluates to true because [] converts to an empty string '', which becomes 0; ![] is false, which also becomes 0, so 0 == 0 is true.

// [] == ![]
[].toString() // ''
Number('') // 0
![] // false
Number(false) // 0
0 == 0 // true
{} == !{}

evaluates to false because {} converts to "[object Object]", which becomes NaN; !{} is false (=> 0), and NaN != 0.

// {} == !{}
({}).toString() // "[object Object]"
Number('[object Object]') // NaN
!{} // false
Number(false) // 0
NaN != 0 // false overall

Further examples such as {} + {}, [] + {}, and {} + [] produce string concatenations according to the same conversion rules; you can experiment at jsisweird.com .

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptStringbooleantype conversiontoStringvalueOfImplicit Conversionnumber
KooFE Frontend Team
Written by

KooFE Frontend Team

Follow the latest frontend updates

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.