Implicit Coercion

Implicit coercion refers to type conversions that are hidden, with non-obvious side-effects that implicitly occur from other actions. In other words, implicit coercions are any type conversions that aren’t obvious (to you).

While it’s clear what the goal of explicit coercion is (making code explicit and more understandable), it might be too obvious that implicit coercion has the opposite goal: making code harder to understand.

Taken at face value, I believe that’s where much of the ire towards coercion comes from. The majority of complaints about “JavaScript coercion” are actually aimed (whether they realize it or not) at implicit coercion.

Note: Douglas Crockford, author of “JavaScript: The Good Parts”, has claimed in many conference talks and writings that JavaScript coercion should be avoided. But what he seems to mean is that implicit coercion is bad (in his opinion). However, if you read his own code, you’ll find plenty of examples of coercion, both implicit and explicit! In truth, his angst seems to primarily be directed at the == operation, but as you’ll see in this chapter, that’s only part of the coercion mechanism.

So, is implicit coercion evil? Is it dangerous? Is it a flaw in JavaScript’s design? Should we avoid it at all costs?

I bet most of you readers are inclined to enthusiastically cheer, “Yes!”

Not so fast. Hear me out.

Let’s take a different perspective on what implicit coercion is, and can be, than just that it’s “the opposite of the good explicit kind of coercion.” That’s far too narrow and misses an important nuance.

Let’s define the goal of implicit coercion as: to reduce verbosity, boilerplate, and/or unnecessary implementation detail that clutters up our code with noise that distracts from the more important intent.

Simplifying Implicitly

Before we even get to JavaScript, let me suggest something pseudo-code’ish from some theoretical strongly typed language to illustrate:

  1. SomeType x = SomeType( AnotherType( y ) )

In this example, I have some arbitrary type of value in y that I want to convert to the SomeType type. The problem is, this language can’t go directly from whatever y currently is to SomeType. It needs an intermediate step, where it first converts to AnotherType, and then from AnotherType to SomeType.

Now, what if that language (or definition you could create yourself with the language) did just let you say:

  1. SomeType x = SomeType( y )

Wouldn’t you generally agree that we simplified the type conversion here to reduce the unnecessary “noise” of the intermediate conversion step? I mean, is it really all that important, right here at this point in the code, to see and deal with the fact that y goes to AnotherType first before then going to SomeType?

Some would argue, at least in some circumstances, yes. But I think an equal argument can be made of many other circumstances that here, the simplification actually aids in the readability of the code by abstracting or hiding away such details, either in the language itself or in our own abstractions.

Undoubtedly, behind the scenes, somewhere, the intermediate conversion step is still happening. But if that detail is hidden from view here, we can just reason about getting y to type SomeType as a generic operation and hide the messy details.

While not a perfect analogy, what I’m going to argue throughout the rest of this chapter is that JS implicit coercion can be thought of as providing a similar aid to your code.

But, and this is very important, that is not an unbounded, absolute statement. There are definitely plenty of evils lurking around implicit coercion, that will harm your code much more than any potential readability improvements. Clearly, we have to learn how to avoid such constructs so we don’t poison our code with all manner of bugs.

Many developers believe that if a mechanism can do some useful thing A but can also be abused or misused to do some awful thing Z, then we should throw out that mechanism altogether, just to be safe.

My encouragement to you is: don’t settle for that. Don’t “throw the baby out with the bathwater.” Don’t assume implicit coercion is all bad because all you think you’ve ever seen is its “bad parts.” I think there are “good parts” here, and I want to help and inspire more of you to find and embrace them!

Implicitly: Strings <—> Numbers

Earlier in this chapter, we explored explicitly coercing between string and number values. Now, let’s explore the same task but with implicit coercion approaches. But before we do, we have to examine some nuances of operations that will implicitly force coercion.

The + operator is overloaded to serve the purposes of both number addition and string concatenation. So how does JS know which type of operation you want to use? Consider:

  1. var a = "42";
  2. var b = "0";
  3. var c = 42;
  4. var d = 0;
  5. a + b; // "420"
  6. c + d; // 42

What’s different that causes "420" vs 42? It’s a common misconception that the difference is whether one or both of the operands is a string, as that means + will assume string concatenation. While that’s partially true, it’s more complicated than that.

Consider:

  1. var a = [1,2];
  2. var b = [3,4];
  3. a + b; // "1,23,4"

Neither of these operands is a string, but clearly they were both coerced to strings and then the string concatenation kicked in. So what’s really going on?

(Warning: deeply nitty gritty spec-speak coming, so skip the next two paragraphs if that intimidates you!)


According to ES5 spec section 11.6.1, the + algorithm (when an object value is an operand) will concatenate if either operand is either already a string, or if the following steps produce a string representation. So, when + receives an object (including array) for either operand, it first calls the ToPrimitive abstract operation (section 9.1) on the value, which then calls the [[DefaultValue]] algorithm (section 8.12.8) with a context hint of number.

If you’re paying close attention, you’ll notice that this operation is now identical to how the ToNumber abstract operation handles objects (see the “ToNumber“” section earlier). The valueOf() operation on the array will fail to produce a simple primitive, so it then falls to a toString() representation. The two arrays thus become "1,2" and "3,4", respectively. Now, + concatenates the two strings as you’d normally expect: "1,23,4".


Let’s set aside those messy details and go back to an earlier, simplified explanation: if either operand to + is a string (or becomes one with the above steps!), the operation will be string concatenation. Otherwise, it’s always numeric addition.

Note: A commonly cited coercion gotcha is [] + {} vs. {} + [], as those two expressions result, respectively, in "[object Object]" and 0. There’s more to it, though, and we cover those details in “Blocks” in Chapter 5.

What’s that mean for implicit coercion?

You can coerce a number to a string simply by “adding” the number and the "" empty string:

  1. var a = 42;
  2. var b = a + "";
  3. b; // "42"

Tip: Numeric addition with the + operator is commutative, which means 2 + 3 is the same as 3 + 2. String concatenation with + is obviously not generally commutative, but with the specific case of "", it’s effectively commutative, as a + "" and "" + a will produce the same result.

It’s extremely common/idiomatic to (implicitly) coerce number to string with a + "" operation. In fact, interestingly, even some of the most vocal critics of implicit coercion still use that approach in their own code, instead of one of its explicit alternatives.

I think this is a great example of a useful form in implicit coercion, despite how frequently the mechanism gets criticized!

Comparing this implicit coercion of a + "" to our earlier example of String(a) explicit coercion, there’s one additional quirk to be aware of. Because of how the ToPrimitive abstract operation works, a + "" invokes valueOf() on the a value, whose return value is then finally converted to a string via the internal ToString abstract operation. But String(a) just invokes toString() directly.

Both approaches ultimately result in a string, but if you’re using an object instead of a regular primitive number value, you may not necessarily get the same string value!

Consider:

  1. var a = {
  2. valueOf: function() { return 42; },
  3. toString: function() { return 4; }
  4. };
  5. a + ""; // "42"
  6. String( a ); // "4"

Generally, this sort of gotcha won’t bite you unless you’re really trying to create confusing data structures and operations, but you should be careful if you’re defining both your own valueOf() and toString() methods for some object, as how you coerce the value could affect the outcome.

What about the other direction? How can we implicitly coerce from string to number?

  1. var a = "3.14";
  2. var b = a - 0;
  3. b; // 3.14

The - operator is defined only for numeric subtraction, so a - 0 forces a‘s value to be coerced to a number. While far less common, a * 1 or a / 1 would accomplish the same result, as those operators are also only defined for numeric operations.

What about object values with the - operator? Similar story as for + above:

  1. var a = [3];
  2. var b = [1];
  3. a - b; // 2

Both array values have to become numbers, but they end up first being coerced to strings (using the expected toString() serialization), and then are coerced to numbers, for the - subtraction to perform on.

So, is implicit coercion of string and number values the ugly evil you’ve always heard horror stories about? I don’t personally think so.

Compare b = String(a) (explicit) to b = a + "" (implicit). I think cases can be made for both approaches being useful in your code. Certainly b = a + "" is quite a bit more common in JS programs, proving its own utility regardless of feelings about the merits or hazards of implicit coercion in general.

Implicitly: Booleans —> Numbers

I think a case where implicit coercion can really shine is in simplifying certain types of complicated boolean logic into simple numeric addition. Of course, this is not a general-purpose technique, but a specific solution for specific cases.

Consider:

  1. function onlyOne(a,b,c) {
  2. return !!((a && !b && !c) ||
  3. (!a && b && !c) || (!a && !b && c));
  4. }
  5. var a = true;
  6. var b = false;
  7. onlyOne( a, b, b ); // true
  8. onlyOne( b, a, b ); // true
  9. onlyOne( a, b, a ); // false

This onlyOne(..) utility should only return true if exactly one of the arguments is true / truthy. It’s using implicit coercion on the truthy checks and explicit coercion on the others, including the final return value.

But what if we needed that utility to be able to handle four, five, or twenty flags in the same way? It’s pretty difficult to imagine implementing code that would handle all those permutations of comparisons.

But here’s where coercing the boolean values to numbers (0 or 1, obviously) can greatly help:

  1. function onlyOne() {
  2. var sum = 0;
  3. for (var i=0; i < arguments.length; i++) {
  4. // skip falsy values. same as treating
  5. // them as 0's, but avoids NaN's.
  6. if (arguments[i]) {
  7. sum += arguments[i];
  8. }
  9. }
  10. return sum == 1;
  11. }
  12. var a = true;
  13. var b = false;
  14. onlyOne( b, a ); // true
  15. onlyOne( b, a, b, b, b ); // true
  16. onlyOne( b, b ); // false
  17. onlyOne( b, a, b, b, b, a ); // false

Note: Of course, instead of the for loop in onlyOne(..), you could more tersely use the ES5 reduce(..) utility, but I didn’t want to obscure the concepts.

What we’re doing here is relying on the 1 for true/truthy coercions, and numerically adding them all up. sum += arguments[i] uses implicit coercion to make that happen. If one and only one value in the arguments list is true, then the numeric sum will be 1, otherwise the sum will not be 1 and thus the desired condition is not met.

We could of course do this with explicit coercion instead:

  1. function onlyOne() {
  2. var sum = 0;
  3. for (var i=0; i < arguments.length; i++) {
  4. sum += Number( !!arguments[i] );
  5. }
  6. return sum === 1;
  7. }

We first use !!arguments[i] to force the coercion of the value to true or false. That’s so you could pass non-boolean values in, like onlyOne( "42", 0 ), and it would still work as expected (otherwise you’d end up with string concatenation and the logic would be incorrect).

Once we’re sure it’s a boolean, we do another explicit coercion with Number(..) to make sure the value is 0 or 1.

Is the explicit coercion form of this utility “better”? It does avoid the NaN trap as explained in the code comments. But, ultimately, it depends on your needs. I personally think the former version, relying on implicit coercion is more elegant (if you won’t be passing undefined or NaN), and the explicit version is needlessly more verbose.

But as with almost everything we’re discussing here, it’s a judgment call.

Note: Regardless of implicit or explicit approaches, you could easily make onlyTwo(..) or onlyFive(..) variations by simply changing the final comparison from 1, to 2 or 5, respectively. That’s drastically easier than adding a bunch of && and || expressions. So, generally, coercion is very helpful in this case.

Implicitly: * —> Boolean

Now, let’s turn our attention to implicit coercion to boolean values, as it’s by far the most common and also by far the most potentially troublesome.

Remember, implicit coercion is what kicks in when you use a value in such a way that it forces the value to be converted. For numeric and string operations, it’s fairly easy to see how the coercions can occur.

But, what sort of expression operations require/force (implicitly) a boolean coercion?

  1. The test expression in an if (..) statement.
  2. The test expression (second clause) in a for ( .. ; .. ; .. ) header.
  3. The test expression in while (..) and do..while(..) loops.
  4. The test expression (first clause) in ? : ternary expressions.
  5. The left-hand operand (which serves as a test expression — see below!) to the || (“logical or”) and && (“logical and”) operators.

Any value used in these contexts that is not already a boolean will be implicitly coerced to a boolean using the rules of the ToBoolean abstract operation covered earlier in this chapter.

Let’s look at some examples:

  1. var a = 42;
  2. var b = "abc";
  3. var c;
  4. var d = null;
  5. if (a) {
  6. console.log( "yep" ); // yep
  7. }
  8. while (c) {
  9. console.log( "nope, never runs" );
  10. }
  11. c = d ? a : b;
  12. c; // "abc"
  13. if ((a && d) || c) {
  14. console.log( "yep" ); // yep
  15. }

In all these contexts, the non-boolean values are implicitly coerced to their boolean equivalents to make the test decisions.

Operators || and &&

It’s quite likely that you have seen the || (“logical or”) and && (“logical and”) operators in most or all other languages you’ve used. So it’d be natural to assume that they work basically the same in JavaScript as in other similar languages.

There’s some very little known, but very important, nuance here.

In fact, I would argue these operators shouldn’t even be called “logical _ operators”, as that name is incomplete in describing what they do. If I were to give them a more accurate (if more clumsy) name, I’d call them “selector operators,” or more completely, “operand selector operators.”

Why? Because they don’t actually result in a logic value (aka boolean) in JavaScript, as they do in some other languages.

So what do they result in? They result in the value of one (and only one) of their two operands. In other words, they select one of the two operand’s values.

Quoting the ES5 spec from section 11.11:

The value produced by a && or || operator is not necessarily of type Boolean. The value produced will always be the value of one of the two operand expressions.

Let’s illustrate:

  1. var a = 42;
  2. var b = "abc";
  3. var c = null;
  4. a || b; // 42
  5. a && b; // "abc"
  6. c || b; // "abc"
  7. c && b; // null

Wait, what!? Think about that. In languages like C and PHP, those expressions result in true or false, but in JS (and Python and Ruby, for that matter!), the result comes from the values themselves.

Both || and && operators perform a boolean test on the first operand (a or c). If the operand is not already boolean (as it’s not, here), a normal ToBoolean coercion occurs, so that the test can be performed.

For the || operator, if the test is true, the || expression results in the value of the first operand (a or c). If the test is false, the || expression results in the value of the second operand (b).

Inversely, for the && operator, if the test is true, the && expression results in the value of the second operand (b). If the test is false, the && expression results in the value of the first operand (a or c).

The result of a || or && expression is always the underlying value of one of the operands, not the (possibly coerced) result of the test. In c && b, c is null, and thus falsy. But the && expression itself results in null (the value in c), not in the coerced false used in the test.

Do you see how these operators act as “operand selectors”, now?

Another way of thinking about these operators:

  1. a || b;
  2. // roughly equivalent to:
  3. a ? a : b;
  4. a && b;
  5. // roughly equivalent to:
  6. a ? b : a;

Note: I call a || b “roughly equivalent” to a ? a : b because the outcome is identical, but there’s a nuanced difference. In a ? a : b, if a was a more complex expression (like for instance one that might have side effects like calling a function, etc.), then the a expression would possibly be evaluated twice (if the first evaluation was truthy). By contrast, for a || b, the a expression is evaluated only once, and that value is used both for the coercive test as well as the result value (if appropriate). The same nuance applies to the a && b and a ? b : a expressions.

An extremely common and helpful usage of this behavior, which there’s a good chance you may have used before and not fully understood, is:

  1. function foo(a,b) {
  2. a = a || "hello";
  3. b = b || "world";
  4. console.log( a + " " + b );
  5. }
  6. foo(); // "hello world"
  7. foo( "yeah", "yeah!" ); // "yeah yeah!"

The a = a || "hello" idiom (sometimes said to be JavaScript’s version of the C# “null coalescing operator”) acts to test a and if it has no value (or only an undesired falsy value), provides a backup default value ("hello").

Be careful, though!

  1. foo( "That's it!", "" ); // "That's it! world" <-- Oops!

See the problem? "" as the second argument is a falsy value (see ToBoolean earlier in this chapter), so the b = b || "world" test fails, and the "world" default value is substituted, even though the intent probably was to have the explicitly passed "" be the value assigned to b.

This || idiom is extremely common, and quite helpful, but you have to use it only in cases where all falsy values should be skipped. Otherwise, you’ll need to be more explicit in your test, and probably use a ? : ternary instead.

This default value assignment idiom is so common (and useful!) that even those who publicly and vehemently decry JavaScript coercion often use it in their own code!

What about &&?

There’s another idiom that is quite a bit less commonly authored manually, but which is used by JS minifiers frequently. The && operator “selects” the second operand if and only if the first operand tests as truthy, and this usage is sometimes called the “guard operator” (also see “Short Circuited” in Chapter 5) — the first expression test “guards” the second expression:

  1. function foo() {
  2. console.log( a );
  3. }
  4. var a = 42;
  5. a && foo(); // 42

foo() gets called only because a tests as truthy. If that test failed, this a && foo() expression statement would just silently stop — this is known as “short circuiting” — and never call foo().

Again, it’s not nearly as common for people to author such things. Usually, they’d do if (a) { foo(); } instead. But JS minifiers choose a && foo() because it’s much shorter. So, now, if you ever have to decipher such code, you’ll know what it’s doing and why.

OK, so || and && have some neat tricks up their sleeve, as long as you’re willing to allow the implicit coercion into the mix.

Note: Both the a = b || "something" and a && b() idioms rely on short circuiting behavior, which we cover in more detail in Chapter 5.

The fact that these operators don’t actually result in true and false is possibly messing with your head a little bit by now. You’re probably wondering how all your if statements and for loops have been working, if they’ve included compound logical expressions like a && (b || c).

Don’t worry! The sky is not falling. Your code is (probably) just fine. It’s just that you probably never realized before that there was an implicit coercion to boolean going on after the compound expression was evaluated.

Consider:

  1. var a = 42;
  2. var b = null;
  3. var c = "foo";
  4. if (a && (b || c)) {
  5. console.log( "yep" );
  6. }

This code still works the way you always thought it did, except for one subtle extra detail. The a && (b || c) expression actually results in "foo", not true. So, the if statement then forces the "foo" value to coerce to a boolean, which of course will be true.

See? No reason to panic. Your code is probably still safe. But now you know more about how it does what it does.

And now you also realize that such code is using implicit coercion. If you’re in the “avoid (implicit) coercion camp” still, you’re going to need to go back and make all of those tests explicit:

  1. if (!!a && (!!b || !!c)) {
  2. console.log( "yep" );
  3. }

Good luck with that! … Sorry, just teasing.

Symbol Coercion

Up to this point, there’s been almost no observable outcome difference between explicit and implicit coercion — only the readability of code has been at stake.

But ES6 Symbols introduce a gotcha into the coercion system that we need to discuss briefly. For reasons that go well beyond the scope of what we’ll discuss in this book, explicit coercion of a symbol to a string is allowed, but implicit coercion of the same is disallowed and throws an error.

Consider:

  1. var s1 = Symbol( "cool" );
  2. String( s1 ); // "Symbol(cool)"
  3. var s2 = Symbol( "not cool" );
  4. s2 + ""; // TypeError

symbol values cannot coerce to number at all (throws an error either way), but strangely they can both explicitly and implicitly coerce to boolean (always true).

Consistency is always easier to learn, and exceptions are never fun to deal with, but we just need to be careful around the new ES6 symbol values and how we coerce them.

The good news: it’s probably going to be exceedingly rare for you to need to coerce a symbol value. The way they’re typically used (see Chapter 3) will probably not call for coercion on a normal basis.