Skip to main content
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with: Johnson Tai
Ben Nadel at cf.Objective() 2011 (Minneapolis, MN) with: Johnson Tai

Coercing Non-Truthy Values In JavaScript

By on

I wasn't sure what to title this post. I didn't want to make the content sound exhaustive, which it isn't; but, I also didn't want it to be so vague that it garnered no interest. Mostly, I just wanted to point out something that I found in the AngularJS source code. Last week, when I was looking at how AngularJS turns a promise-resolution into an AJAX .abort() request, I saw a very interesting IF-statement that used non-truthy type coercion.

In the recent releases of AngularJS, the $http service can accept a "timeout" property that is either a number or a promise. If it's a number, it represents the timeout, in milliseconds, after which the underlying AJAX request will be aborted. If it's a promise, AngularJS will abort the AJAX request if and when the promise is resolved. In the code, AngularJS handles the type-check using this IF-statement:

// Check for timeout in milliseconds.
if ( timeout > 0 ) {

	// ...

// Check for timeout as a Promise object.
} else if ( timeout && timeout.then ) {

	// ...

}

Each condition, on its own, is rather straightforward - the "happy paths" make sense. But, what happens in the first condition when the timeout is a promise? Suddenly, we're comparing an object to a number.

In my personal experience, the vast majority of type coercion has involved treating a value as either True(ish) or False(ish). To me, this kind of coercion is easy to understand. But, when I saw the above "greater than" comparison, between an object and a number, I didn't really have any idea what to expect.

After some Googling, I found some resources that seemed to indicate that when JavaScript compares an Object to a non-Object, it does so by first converting the Object into a "primitive" using this kind of approach:

object.valueOf().toString()

If we apply this to an Object in JavaScript:

( {} ).valueOf().toString()

... we get:

"[object Object]"

Now that we've converted it to a String, we can compare it to a Number by converting the String to a Number:

Number( "[object Object]" )

... which gives us:

NaN // Not a Number.

... which means that our Promise vs. Zero condition is ultimately converted into:

if ( NaN > 0 ) { ... }

... which is always false since NaN is never equal to anything, not even NaN.

Now, this gets really interesting when we use an Array instead of an Object:

Number( ( [ ] ).valueOf().toString() ); // .... Gives us 0.
Number( ( [ 1 ] ).valueOf().toString() ); // .... Gives us 1.
Number( ( [ 1, 2 ] ).valueOf().toString() ); // .... Gives us NaN.

... which means:

console.log( [] == 0 ); // .... true.
console.log( [ 1 ] == 1 ); // .... true.
console.log( [ 1 ] > 0 ); // .... true.

In an abstract sense, I think the IF-statement (that prompted this post) is very interesting and very clever. But, part of me also believes that it violates the "principle of least surprise." Of course, this may just represent my narrow understanding of type-coercion in JavaScript; after all, I rock truthy type-coercions like a boss. Perhaps, with time, this type of coercion (no pun intended) will also seem like second-nature.

UPDATE: It looks like I may have misinterpreted the ECMAScript spec. I have updated this blog post based on the comments by Kirill Dmitrenko, below.

Want to use code from this post? Check out the license.

Reader Comments

27 Comments

Another solution would have been to just reverse the if statements... if timeout has "then", we know it's a promise. Then we don't need to bother confusing programmers.

15,688 Comments

@Jonathan,

Good point, switching the conditions would definitely have made this a bit more intuitive. But, at least it was an opportunity to dive a little deeper :)

2 Comments

> After some Googling, I found some resources that seemed to indicate that
> when JavaScript compares an Object to a non-Object, it does so by first
> converting the Object into a "primitive" using this kind of approach:
> object.toString().valueOf()

No, all relative expressions only do .valueOf(), see paragraph 11.8 of Ecma-262 specification:

dmikis-osx2:~$ node
> a = {
... toString: function () { return 'a'; },
... valueOf: function () { return 2; }
... }
{ toString: [Function],
valueOf: [Function] }
> b = {
... toString: function () { return 'b'; },
... valueOf: function () { return 1; }
... }
{ toString: [Function],
valueOf: [Function] }
> a > b
true

15,688 Comments

@Kirill,

This stuff is really hard for me to wrap my head around. I tried looking at the spec, but I have trouble understanding it. If you have an object "a", then it seems that .valueOf() is the same as the root object:

var a = {};
console.log( a === a.valueOf() ); // ... true

If that's the case, it seems that the .valueOf() doesn't get the runtime closer to a comparison. It must be doing _something_ after it gets the value in order to make the comparison, no?

It hurts my brain even more if you sub-class the Array to create an object:

var a = Object.create( Array.prototype );
a.push( 1 );
console.log( a > 0 ); // .... true

It will take a lot more time for to really let this all sink in.

15,688 Comments

@Kirill,

After re-reading the original resources that I had, it looks like I got the method calls confused, at least in the way that I understand it. You are right - the .valueOf() would be called first:

When the [[DefaultValue]] method of O is called with hint Number, the following steps are taken:

1. Call the [[Get]] method of object O with argument "valueOf".
2. If Result(1) is not an object, go to step 5.
...
5. Call the [[Get]] method of object O with argument "toString".

I'll update the post to correct.

1 Comments

Thank you very much for the information.

The first time I read about how coercion works in Javascript was with Angus Croll:

http://javascriptweblog.wordpress.com/2011/02/07/truth-equality-and-javascript/

There is also a link at the end of this article presenting something similar to your references links to bclary:

http://webreflection.blogspot.ca/2010/10/javascript-coercion-demystified.html

Some great people like Douglas Crockford advocate to never use coercion; always use strict equality comparison but Angus Croll convince me to fall in love with coercion in Javascript.

2 Comments

@Ben,

My pleasure.

I personally try to avoid sophisticated type casts in my code. As John Carmack says, if something can be done wrong, it eventually will be. And, I think, type coercing is such situation.

15,688 Comments

@Kirill,

I think I agree. With the exception that I do rather enjoy truthy type coercions, even on the empty string:

if ( "" ) { ... }

But, the rules for those kinds of coercions are very straightforward - not like the stuff of this blog posts where it's a much complicated decision tree. Crazy stuff.

I believe in love. I believe in compassion. I believe in human rights. I believe that we can afford to give more of these gifts to the world around us because it costs us nothing to be decent and kind and understanding. And, I want you to know that when you land on this site, you are accepted for who you are, no matter how you identify, what truths you live, or whatever kind of goofy shit makes you feel alive! Rock on with your bad self!
Ben Nadel