Type coercion, type conversion, typecasting, and type juggling: all different names that refer to the process of converting one data type into another. This process is present in almost every programming language and is an important concept in computer science.
While JavaScript is known as one of the easiest programming languages for beginners, it can also become frustrating when developers expect JavaScript to do one thing and it returns another.
And while the ECMAScript Language Specification standard is available to developers to help guide them through these issues, not reading and understanding the specification standard can cause more frustration when dealing with type coercion in JavaScript.
Type coercion in JavaScript is an example of how the language can produce unexpected results when we don’t know exactly how it works. Everyone who has touched even a little bit of code in JavaScript can relate that type coercion can be tricky.
While the code examples we cover in this article may seem like bugs that a version update could fix, the reason we review why type coercion acts the way it does is because many legacy products and code depend on older versions of JavaScript.
This means that creating changes to the language can break legacy products and code, and we must instead learn how to navigate the quirks of type coercion.
In this post, we’ll cover what types are in JavaScript and how to use type coercion, setting us up for programming success.
We can refer to JavaScript as an untyped language, which means that it has no static types. However, the prominent myth that JavaScript doesn’t have types is false.
JavaScript has seven primitive types:
string
number
Boolean
undefined
null
Symbol
BigInt
Variables in JavaScript don’t have types, however. Whenever we use the typeof
operator to return a variable type, we return the variable’s value type.
Now that we have cleared up a few misconceptions about JavaScript and types, we can learn more about type coercion and how it works in JavaScript.
Type coercion in JavaScript only coerces to the string
, number
, and Boolean
primitive types. There’s no way in JavaScript to coerce a value type to object
or function
.
JavaScript has two characterized forms of coercion: implicit coercion and explicit coercion.
Implicit coercion happens when JavaScript coerces the value type to the expected type under the hood. This type of coercion happens without the developer noticing.
Explicit coercion happens when we want to coerce the value type to a specific type. Most of the time, explicit coercion in JavaScript happens using built-in functions such as String()
, Number()
, and Boolean()
.
When we try to create operations in JavaScript using different value types, JavaScript coerces the value types for us implicitly.
This is one of the reasons why developers tend to avoid implicit coercion in JavaScript. Most of the time we get unexpected results from the operation if we don’t know exactly how JavaScript coerces the value types.
Implicit coercion is not as bad as developers tend to think, and in fact, is useful for writing readable but efficient code. The key to properly understanding how implicit coercion works in JavaScript is to understand what it is doing under the hood.
There are many possible ways for coercing a primitive type to a number. The Number()
function coerces the value type that passes to the function, then to a number. When a type can’t be coerced to a number, the returned result is NaN
.
Let’s look at a few examples of explicit coercion using the Number()
function:
Number("42"); // 42 Number(""); // 0 Number(true); // 1 Number(false); // 0 Number(null); // 0 Number(undefined); // NaN
We can clearly see some obvious and unexpected results. Converting null
to a number returns 0
while converting undefined
to a number returns NaN
. Both operations should return NaN
since both value types are clearly not valid numbers.
Converting an empty string to a number returns 0
. This is another weird part of JavaScript because this value type is clearly not a valid number yet it still converts to 0
.
Kyle Simpson, the creator of the You Don’t Know JS book series, said, “Empty string becoming 0 is the root of all coercion evil.”
Although the results that we get from the Number()
function may seem unexpected, the ECMAScript specification clear states these discrepancies. But without reading the ECMA specification, developers may not realize this is just how JavaScript is written.
In our first example, we received different results for null
and undefined
. The ECMAScript specification Number()
function with a null value type it returns 0
, and whenever we use the same function with undefined
it returns NaN
.
ToNumber
is a type conversion name that the ECMAScript specification uses when referring to an operation where a value converts to a number. Number()
is a primitive wrapper object in JavaScript that converts a value to a number. This is the same with ToBoolean
, which we will cover later.
Below is a list of arguments and the result the ToNumber
operation converts them to:
In our other example, we used the Number()
function with an empty string and received a 0
. This is something that is explicit in the ECMAScript specification as well:
A
StringNumericLiteral
that is empty or contains only white space is converted to +0. – ECMAScript 2015 Language Specification
To explicitly coerce a value to a string in JavaScript we can use the String()
function. To implicitly coerce a value to a string, we can use the +
operator with any operand that is a string.
The primitive types convert to strings as expected:
String("42"); // "42"
String(true); // "true"
String(false); // "false"
String(null); // "null"
String(undefined); // "undefined"
We should be careful when using type coercion when we want to create an operation and one of our operand types is a string.
JavaScript returns our operation as a string when the correct way of handling the operation should be throwing an error because there’s no way to make a mathematical operation using a number and a string, which is not a valid number:
10 + "10" // "1010" 20 + "200" // "20200" 0.212 + "1" // "0.2121"
To explicitly coerce a value to Boolean in JavaScript, we can use the Boolean()
function. To implicitly coerce a value to Boolean, we can use logical operators, such as ||
, &&
, and !
in a logical context.
The specification of the Boolean() function
is very clean and helpful. We can clearly see which results we receive depending on the value type that we pass:
The list of falsy values is easy to remember. Everything that is not on the list is a truthy value:
Boolean('') // false Boolean(0) // false Boolean(-0) // false Boolean(NaN) // false Boolean(null) // false Boolean(undefined) // false Boolean(false) // false
As stated before, logical operators also coerce a value type to a Boolean:
true && false // false true && true // true true || false // true true || !false // true "name" || 0 // "name" "name" || [] // "name" "" || [1, 2, 3] // [1, 2, 3]
Type coercion is a core JavaScript concept used across every application, API, and service using JavaScript.
In all, unless you pursue explicit coercion, JavaScript implicitly coerces depending on the value types and operation used. But regardless of using implicit or explicit type coercion, it provides developers flexibility and helps make the code more readable.
This short overview provides the basics in understanding type coercion, however, reading the ECMAScript specifications can provide a more in-depth review of the concept to understand why unexpected type coercion results occur.
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.