JavaScript is a dynamically typed — also known as untyped — language. Typing is the process of defining the data type of a variable. In dynamically typed languages, typing is associated with the variable’s value and not the variable itself. Thus, in JavaScript, variables don’t need to specify a certain data type when they are declared.

The variable five in the example doesn’t specify the data type. We can assign a String value or a Number, or any value for that matter later on.
In a statically typed language, the variable declaration statement first states the data type of the variable, followed by its name. Bellow is an example of the statically typed language Java.

Here, the age variable can only be an integer.
JavaScript, on the other hand, tackles the issue of type later, after a value has been assigned to the variable.
Type Conversion
In computer science, there are different ways of changing an expression from one type to another.
Let’s see some extreme examples of type conversion in JavaScript.

We see that JavaScript automatically converts types in the above operations.
Regarding the last one. The strings are converted into numbers and then the subtraction of a negative number is equivalent to adding the positive version of that number.
Examples like the above are available in wtfjs.com.
The ability to convert data types is fundamental to all programming languages since it ensures the compatibility of data within a program. In JavaScript, values are converted from one data type to another with the help of coercion and casting.
What is Coercion?
Coercion is a mechanism that implicitly converts types when performing certain operations. In order for an operation with two operands of different type to make sense, JavaScript converts one of the two.

Here The typeof operator is used to determine the data type of value. In this example, the empty string “” is of type String. The unary plus operator + is used before the typeof expression. It converts the result of typeof “” into a Number. But since in our example, the string “” cannot be directly converted into a Number, the unary plus operator coerces it to NaN (Not-a-Number).
The concatenation operator + is used to combine the string “I am a “ with the coerced value NaN. The final result is the string “I am a number” because the NaN value, which is a Number, is implicitly converted (coerced) into a String during the concatenation process in order for the concatenation to make sense.
In JavaScript, data types, both primitives and objects, can be coerced into only three types: Numbers, Strings and Booleans.
Let’s see each of them in action.
String coercion
String coercion is triggered by the binary plus operator. The binary plus operator results in string concatenation in all cases except for when we have both operands as numbers. In that case is always a numeric addition.

We must take into account string concatenation when we receive data from input forms and want to implement mathematic addition.
Boolean coercion
Boolean coercion can lead to only two possible results. True or False.
We call the value types that when converting to booleans result in False, falsy, and all the other values, that respectively result in True, truthy.
There is a pretty short list of values that when converted to a boolean became False.
They are:

Implicit Boolean conversion can occur when non-boolean values are evaluated in a boolean context. That would be a conditional statement or an evaluation with logical operators.
Here some examples of converting values to Booleans.

In the example above, the first if statement evaluates to true since a String is not falsy, thus truthy, and the corresponding message is logged.
The second if statement evaluates to false because 0 is falsy, so the message is not logged.

In this second example, we have an expression with logical operators.
In JavaScript, contrary to other C family languages, logical operators return the original operands and not the boolean values. But the values are internally coerced to boolean so that the logical operation makes sense.
An expression with more than one logical operator evaluates to the value of the last evaluated operand. The && and || operands are evaluated left to right.

Regarding the logical AND and logical OR evaluations in the example. The AND operator has a higher precedence over the OR operator, meaning the && operator is executed before the || operator. It evaluates to true so {} is returned. The logical OR (||) operator for a set of operands is true if and only if one or more of its operands are true. That is the case in the last evaluation, so the truthy value {} is returned.
Numeric coercion
Numeric coercion is triggered by most operators.
Bitwise operators:
Bitwise operators in JavaScript work directly with the binary versions of numbers.
They work on 32-bit integers, converting operands to binary, performing the operation bit by bit, and returning the result as a decimal number.
Here’s an example of the XOR JavaScript bitwise operator:

Comparison operators:
Booleans often result from comparison operators and are combined using logical operators to form complex conditions:

Arithmetic operators:
Arithmetic operators will trigger a numeric conversion except for the plus operator which will not trigger a numeric conversion if any of the two operands is a String.

Loose equality operator
Loose equality, or double equals, operator will trigger numeric conversion before comparing the operands. But this rule will not apply when both of the operands are String s.

In line 10, true is coerced to NaN and true to 1, so we have NaN == 1 which evaluates to false. Next, false is coerced to 0 and false to NaN, so we have 0 == NaN which is false.
Regarding the expression in the last line, this is the exception. No numeric conversion is triggered. If both strings were coerced to numbers, we would have NaN == NaN, which is false, but here we get true.
Remember that loose equality won’t trigger numeric conversion when is applied to null or undefined, since both null and undefined equal only to null or undefined and nothing else.
What is Casting?
Sometimes we want to be explicit and tell the language, ‘I want you to convert the value type regardless of it making sense or not’.
It is an explicit programming decision we make, and in this case the type conversion mechanism is called casting.
Lets examine our previous example again.

We see that with the use of the unary operator we convert the String after the typeof operator to a Number. The code could make sense as is, without the use of the plus operator:

But we want to explicitly convert it to a Number, thus the use of the unary operator. This is an example of casting.
In a nutshell, type casting is the manual conversion of one data type to another using explicit functions or operators.
All types of conversion can be achieved with type casting.
Explicit Numeric Conversion
Number constructor
When the Number constructor is called as a function Number (parameter), it coerces the parameter to a Number primitive. If coercion is not workable, it returns NaN.

Using the Number() function is the only way to convert a BigInt to a Number.

parseInt()
The parseInt() function is used to convert a value into an integer of a specified radix.
Syntax:

The radix shows the base in mathematical numeral systems.
This parameter is optional. If not provided, the default radix is 10, which is the base for the decimal numerical system.

The only exception to this rule is if the input string, with leading white-space and possible +/- signs removed, begins with a zero, followed by lowercase or uppercase X (0x or 0X). parseInt ignores any value after decimal.
Εxample:

parseFloat()
By using the parseFloat() function, we can convert a String to a floating point number.

Unary Operators
As we know from Math, unary operators are operators with only one operand. The unary operators that we can use to trigger numeric conversion are unary plus (+operand) and unary minus (- operand).
When we apply the unary plus operator to a non numeric value, it attempts to convert this value to a Number. If the operand cannot be converted to a Number, the result of the unary plus operation will be NaN. If the operand is already a Number, it remains unchanged.

The unary minus operator (- operand) can also trigger numeric conversion. It performs the same numeric conversion as the unary plus operator, but it additionally changes the sign of the resulting number.

Unary operators, contrary to Number function, will throw a TypeError when encountering a BigInt.
Explicit Boolean Conversion
Explicit boolean conversion involves explicitly converting a value to a boolean using functions or operators.
Here are two common methods:
Boolean(parameter)

In the example above, the Boolean() function explicitly converts the numeric value 5 to the boolean value true.
Double negation (!!) operator
The double negation casts to boolean. It is not an actual operator but two negation operators in a raw. The first ! negates once, then the other ! negates the value once more. This way if the parameter is truthy we get true as a result. If not, we get false.

In this example, the double negation operator !! is used to explicitly convert the strings “Hello” and “” to the boolean value true and false, respectively.
Explicit String Conversion
String Constructor
When the String() constructor is called as a function with a parameter, it converts the parameter value to a primitive string. The below examples show how you can use String() function to cast different types of values to String:
Converting objects
So far, we have looked at examples of type conversions regarding primitives. But in the last two examples, the data converted to a String was an Object.
We have mentioned that objects, just like primitives, can also be converted to a different data type. When objects are used in operations that expect primitive values, type coercion arises.
So when JavaScript encounters a reference type in a place where it expected a primitive, it doesn’t fail, but it uses an internal fallback mechanism that converts the reference type to a primitive. This mechanism is the build-in method [toPrimitive].
First, objects are converted into primitives and then they are converted again to one of the three types (String, Number, Boolean).

to numbers results in converting them to strings first. This will make the non-primitive become primitive, i.e. String. Then this primitive String is further coerced to a Number if an operand dictates so.

Below we see the steps JavaScript follows to the final coercion.

In the example, each value in the expression is first coerced into a String, then the coercion progress further.
Another more simple example below:

And therefore it logs true:

Regarding Equality
The loose equality operator, as we’ve seen so far, is the equality operator that allows type coercion. There is also a second equality operator, the triple equals or strict equality operator (===), that doesn’t allow coercion to take place.

We use the latter operator when we don’t want JavaScript to implicitly convert from one type to another. Note that type conversion can be achieved with the strict equality operator, but only explicitly.
Which one to use, type casting or coercion?
Most favor type casting for being more readable and more predictable.
Coercion can produce surprising results that make some programmers talk about coercion as being evil. Of course, coercion is not evil, nor does it have to be surprising. Completely avoiding implicit coercion is not recommended.
Coercion can be a powerful tool when used consciously and appropriately. And the element of surprise actually comes from a lack of understanding of how coercion works. There are cases where coercion can improve the readability of our code.
We can illustrate this with an example:

Here, since double equals permits coercion, the developer2.experience property which is undefined is coerced to null, so the condition is met. Should we use the triple equals operator, which does not permit coercion, we would be much more verbose.

So although most times we should favor type casting, this is not a rule written in stone. Understanding both implicit and explicit conversion is crucial to handle different scenarios and make informed programming decisions.