JavaScript type conversions explained
Javascript is a weakly typed language, which means different datatypes for variables and objects are not explicitly specified, but done implicitly by the Javascript engine at compile time. Leaving this critical task to the Javascript engine sometimes causes problems in our programs that we are not even aware of. This is why it is important to know how type conversion works in Javascript, which is the main source of logical errors.
In this article we will learn:
- What is type conversion?
- How primitive and object types are converted
- The difference between implicit and explicit type conversion
- Why you should use
===
instead of==
- The algorithm Javascript uses to convert objects to primitives
- What object-to-primitive conversion methods are and how they work
What is type conversion?
Type conversion as the name implies is simply converting a value from one type to another in Javascript. Both primitive values and objects are both strong candidates for type conversion in Javascript. For Example:
20 + "twenty" // => "20 twenty": Number 20 converts to a string
"10" * "10" // => 100: Both strings convert to numbers
let y = 2 - "x" // n == NaN; String 'x' can't convert to a number
y + "values" // => "NaN values": NaN converts to string "NaN"
Javascript uses the strict equality(===
) and loose equality(==
) comparison operators to test for equality between two values, but type conversion only occurs when the loose equality operator is used. When testing for strict equality using ===
, both the type and value of the variables we are comparing have to be the same.
10 === 10 // true
NaN === NaN // false
In the preceding code, the number 10
is compared with the number 10
, and as expected the expression returns true
. Both are numbers, and share the same value of 10
. The only exception to this rule is the NaN
value, a value of a unique numeric type in Javascript. Two NaN
values are never equal to each other.
On the flip side, the loose equality operator(==
) is quite different. When used to compare two values, the two values are compared only after they have been converted to a common type.
'20' == 20 //true
false == 0 // true
The values in the preceding code are converted (coerced) to a common type before they are compared. Whenever type conversion occurs in Javascript, the value(primitive or object) involved can only be converted to strings, numbers, or booleans. Though the conversion logic for primitives and object varies, they are both converted to only these three types.
Explicit type conversion
When the developer chooses consciously to convert from one type to another, this is known as an explicit type conversion(type casting). The simplest way to perform an explicit type conversion is to use the Boolean()
, Number()
, and String()
functions:
Number("5") // 5
String(true) // "true"
Boolean([]) // true
All values except null
and undefined
has a toString()
method and the result of this method is usually the same as that returned by the String()
function.
Implicit type conversion
Implicit type conversions are performed by certain operators and are sometimes used explicitly for the purpose of type conversion. However, an implicit conversion can also be triggered by a surrounding context. For example using if (value) {...}
, here value
is converted to boolean.
x + "" // String(x)
2 + "" // "2", String(2)
+x // Number(x)
x-0 // Number(x)
!x // Boolean(x)
!2 // false, Boolean(!2)
The ! operator converts its operand to boolean and negates it.
How primitive types are converted
Remember, there are two types in Javascript: Primitives and Object Types. Let’s understand how type conversion works in primitive types.
The conversion of a primitive value is simple, as seen in the preceding table. The Javascript engine uses the Boolean()
, Number()
and String()
functions to convert primitive values.
String conversion
String(1) // '1'
String("0") // '0'
String("one") // 'one'
String(true) // 'true'
String(false) // 'false'
String(null) // 'null'
String(undefined) // 'undefined'
String() // ''
String('') // ''
String(' ') // ' '
Numeric conversion
Number(1) // 1
Number("0") // 0
Number("one") // NaN
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number() // 0
Number('') // 0
Number(' ') // 0
Boolean conversion
Boolean(1) // true
Boolean(0) // false
Boolean("one") // true
Boolean(true) // true
String(false) // false
Boolean(null) // false
Boolean(undefined) // false
Boolean() // false
Boolean('') // false
Boolean(' ') // true
The following table summarizes type conversion in primitive types:
Value | String Conversion | Numeric Conversion | Boolean Conversion |
---|---|---|---|
1 | “1” | 1 | true |
0 | “0” | 0 | false |
“1” | “1” | 1 | true |
“0” | "0" | 0 | true |
“one” | "one" | NaN | true |
true | “true” | 1 | true |
false | “false” | 0 | false |
null | “null” | 0 | false |
undefined | “undefined” | NaN | false |
” | ” | 0 | false |
’ ’ | ’ ’ | 0 | true |
Open Source Session Replay
OpenReplay is an open-source, session replay suite that lets you see what users do on your web app, helping you troubleshoot issues faster. OpenReplay is self-hosted for full control over your data.
Start enjoying your debugging experience - start using OpenReplay for free.
How object types are converted
Object-to-primitive conversion occurs in the context where an object is used instead of a primitive value. For example, when objects are added obj_1 + obj_2
, substracted obj_1 - obj_2
or printed using alert(obj)
. Javascript uses the Toprimitive
algorithm for object-to-primitive conversion. This algorithm allows you to choose how an object should be converted(using one of the object-to-primitive conversion methods) depending on the context in which the object is being used (which is determined through hints).
Conceptually, the [ToPrimitive]
algorithm can be said to be the composite of two aggregate parts: Hints and Object-to-primitive conversion methods.
Hints
Hints are signals the [ToPrimitive]
algorithm uses to determine what an object should be converted to in a particular context. There are three variants:
"string"
: In the context where an operation expects a string, if a conversion to string is possible, likealert()
or the built-inString()
function.
// output
alert(obj);
//output
String(obj)
// using object as a property key
anotherObj[obj] = 1000;
"number"
: In the context where an operation expects a number if such a conversion is possible.
// explicit conversion
let num = Number(obj);
// maths (except binary plus)
let x = +obj; // unary plus
let difference = Date1 - Date2; // date objects
// less/greater comparison
let less = Obj1 < obj2;
"default"
: Happens in rare cases, in the context where an operator expresses no preference about what type of primitive value is expected; i.e., it is “not sure” of what type to expect.
For example, the binary +
operator works both with strings(concatenates them) and numbers(adds them), so the object can be converted to both a string or number in this case. Or when an object is compared to a string, number, or symbol using the loose equality ==
operator.
// binary plus
let sum = obj1 + obj2;
// obj == string/number/symbol
if (obj == 10 ) { ... };
All built-in objects (except Date) implement the "default"
hint as "number"
. Date implements the "default"
hint as "string"
.
N/B: There are only three hints. It’s that straightforward. There is no “boolean” hint (all objects are true in boolean context) or anything else. And if we treat "default"
and "number"
the same, as most built-in objects do, then there are only two conversions.
Object-to-primitive Conversion Methods
After the [ToPrimitive]
algorithm has identified the primitive value an object should convert to based on the hint. The object is then converted to the primitive value using Object-to-primitive conversion methods.
There are three variants:
- toString/valueOf: The
toString()
andvalueOf()
are inherited by all objects in Javascript. They are used for the sole purpose of object-to-primitive conversions. The[ToPrimitive]
algorithm first tries thetoString()
method. If the method is defined it returns a primitive value, then Javascript uses the primitive value(even if it is not a string!). IftoString()
returns an object or does not exist, then Javascript tries thevalueOf()
method. If that method exists and returns a primitive value, Javascript uses that value. Otherwise, the conversion fails with aTypeError
.
toString -> valueOf
for “string” hint.valueOf -> toString
otherwise.
let Person = {
name: "Mary",
age: 22,
// for hint="string"
toString() {
return `{name: "${this.name}"}`;
},
// for hint="number" or "default"
valueOf() {
return this.age;
}
};
alert(Person); // toString -> {name: "Mary"}
alert(+Person); // valueOf -> 22
alert(Person + 10); // valueOf -> 32
In the preceding code, Person
becomes a self-descriptive string or number depending on the conversion context. The toString()
method is used for conversion for hint="string"
and valueOf()
is used otherwise("number" or "default"
).
However, you may want to handle all your conversions in a single place. In that case, you can implement only the toString()
method like this:
let Person = {
name: "Mary",
toString() {
return this.name;
}
};
alert(Person); // toString -> Mary
alert(Person + 1000); // toString -> Mary1000
- Symbol.toPrimitive: Unlike the
toString()
andvalueOf()
methods that have existed since antiquity, theSymbol.toPrimitive
is a well-known Symbol in ES6. It allows you to override the default object-to-primitive conversion (where thetoString()
andvalueOf
methods are used by the[ToPrimitive]
algorithm) in Javascript and define how you want objects to be converted to primitive values. To do this define a method with this symbolic name like this:
obj[Symbol.toPrimitive] = function(hint) {
// return a primitive value
// hint = one of "string", "number", "default"
}
For example, here the Person
object does the same as above using Symbol.toPrimitive
let Person = {
name: "Mary",
age: 22,
[Symbol.toPrimitive](hint) {
alert(`hint: ${hint}`);
return hint == "string" ? `{name: "${this.name}"}` : this.age;
}
};
// conversions demo:
alert(Person); // hint: string -> {name: "Mary"}
alert(+Person); // hint: number -> 22
alert(Person + 10); // hint: default -> 32
In the preceding code, we can see that the single method Person[Symbol.toPrimitive]
handles all conversion cases.
N/B: In the absence of Symbol.toPrimitive
and valueOf()
, toString()
will handle all primitive conversions.
Object-to-boolean conversions
All objects convert to true in Javascript—including the wrapper object new Boolean(false)
and empty array. The object-to-primitive algorithm is not required for object-to-boolean conversion.
Object-to-string conversions
When an object needs to be converted to a string, Javascript first converts it to a primitive using [ToPrimitive]
algorithm(hint "string"
), then converts the derived primitive to a string.
For instance, if you pass an object to a built-in function that expects a string argument like String()
or when objects are interpolated in template literals.
Object-to-number conversions
When an object needs to be converted to a number, Javascript first converts it to a primitive using [ToPrimitive]
algorithm(hint "number"
), then converts the derived primitive to a number. Built-in Javascript functions and methods that expect numeric arguments convert object arguments to numbers this way, e.g Math()
.
Special case operator conversions
Type conversion also occurs when the operand of certain Javascript operators is an object, Let’s have a detailed look at these operators.
-
The
+
operator: This operator performs numeric addition and string concatenation. if either of its operands is an object, they are converted to primitive values using the[ToPrimitive]
algorithm(hint"default"
). Their types are checked, once they are converted to primitive values. If either argument is a string, it converts the other to a string and concatenates the strings. Otherwise, it converts both arguments to numbers and adds them. -
The
==
and!==
operators: These operators perform equality and inequality testing in a loose way that enables type conversions. If one operand is an object and the other is a primitive value, these operators convert the object to primitive using the[ToPrimitive]
algorithm(hint"default"
) and then compare the two primitive values. -
The
<,<=,>
and>=
relational operators: The relational operators compare the relationship between two values and can be used to compare both numbers and strings. If either operand is an object, it is converted to a primitive value using the[ToPrimitive]
algorithm(hint"number"
). However, unlike the object-to-number conversion, the primitive values returned are not converted to numbers(since they are compared and not used).
Conclusion
Now you know how type conversion works in Javascript and how to use it. You are more confident and aware of where to use it and when Javascript implicit converts between different types(especially object-to-primitive conversions).
A TIP FROM THE EDITOR: For more on internal details of JavaScript, check our previous JavaScript Types and Values, explained article.