Types, coercion and the == versus === argument
Module 1 left you with a promise: "5" + 2 gives "52", "5" - 2 gives 3,
and neither warns you. Here is why, and here is the rule that stops it costing
you a wrong total on a real page.
What a value can be
Every value in JavaScript is one of eight things. Seven are primitives — single, simple values — and everything else is an object.
| Type | Example | Notes |
|---|---|---|
number |
80, 2.5, -7 |
One type for whole and decimal. No separate integer. |
string |
'Priya', "411014" |
Text. Single or double quotes, no difference. |
boolean |
true, false |
|
undefined |
undefined |
"No value has been set here yet." |
null |
null |
"Deliberately empty." Set by you, not by the language. |
bigint |
9007199254740993n |
Whole numbers too large for number. Rare. |
symbol |
Symbol('id') |
Unique keys. You will not need it for a long time. |
object |
{}, [], functions, dates |
Everything else. Module 4. |
undefined and null both mean "nothing", which seems wasteful until you use
the distinction: undefined is the language saying nothing was provided;
null is you saying deliberately empty. A tiffin order with no delivery note
has note: null if you set it so, and undefined if you forgot the field.
Asking what something is
console.log(typeof 80);
console.log(typeof 'Priya');
console.log(typeof true);
console.log(typeof undefined);
number
string
boolean
undefined
So far so reasonable. Now two you have to know about:
console.log(typeof null);
console.log(typeof [1, 2, 3]);
object
object
typeof null is "object", and that is a bug. It dates from 1995, it is
wrong, and it cannot be fixed because too much existing code depends on it. It
is the most famous wart in the language. Check for null with value === null.
Arrays reporting "object" is not a bug — an array genuinely is a kind of object
— but it is unhelpful. Use Array.isArray():
console.log(Array.isArray([1, 2, 3]));
console.log(Array.isArray('dal'));
true
false
Dynamic typing, and what it costs
A variable does not have a type; the value in it does. This is legal:
let x = 80;
x = 'Priya';
x = [1, 2, 3];
No error at any point. This is called dynamic typing, and it is genuinely convenient early on — no declarations to write, nothing to satisfy.
Name the cost honestly. Nothing checks that the value in a variable is what the next line expects. A function written for a number receives a string, does something plausible-looking with it, and returns a wrong answer rather than an error. Every bug in the rest of this lesson has that shape.
This is the entire reason TypeScript exists, and why the TypeScript course on this site is worth taking after this one. For now, the defence is knowing the conversion rules.
Coercion: the rule behind "52"
When you combine two values of different types, JavaScript converts one rather than complaining. That conversion is coercion, and there is essentially one rule to remember.
+ joins text if either side is text. Every other arithmetic operator converts
both sides to numbers.
console.log('5' + 2);
console.log('5' - 2);
console.log('5' * 2);
console.log('5' / 2);
52
3
10
2.5
+ is doing two jobs — addition and joining text — and text wins whenever it is
present. -, * and / have no meaning for text, so both sides become numbers.
Order matters, because it evaluates left to right:
console.log(1 + 2 + '3');
console.log(1 + '2' + 3);
33
123
The first adds 1 and 2 to get 3, then joins '3'. The second joins immediately
and never does arithmetic again.
Here is the full conversion table. It is worth coming back to.
| Value | Number(value) |
String(value) |
Boolean(value) |
|---|---|---|---|
'12' |
12 |
'12' |
true |
' 12 ' |
12 |
' 12 ' |
true |
'12abc' |
NaN |
'12abc' |
true |
'' |
0 |
'' |
false |
'0' |
0 |
'0' |
true |
true |
1 |
'true' |
true |
null |
0 |
'null' |
false |
undefined |
NaN |
'undefined' |
false |
[] |
0 |
'' |
true |
Two rows in that table cause real bugs. Number('') is 0, not NaN — an
empty form field converts to a perfectly valid zero. And '0' is truthy,
because it is a non-empty string, which the next lesson returns to.
The real bug: everything from a form is a string
This is not a curiosity. It is the bug you will actually write.
A number typed into a web page arrives as text — always, even from
<input type="number">. So:
const platesFromForm = '10';
console.log(platesFromForm + 5);
105
Ten plates plus five more is a hundred and five. No error, no warning, and on a billing page a customer is charged for 105 plates.
It gets quieter than that. Comparisons:
console.log('10' > '9');
console.log(10 > 9);
false
true
Ten is not greater than nine. When both sides are strings, JavaScript
compares them alphabetically, character by character — and '1' comes before
'9', so '10' sorts before '9' exactly as "apple" sorts before "banana". A
stock check written as if (quantity > minimum) with two form values silently
does the wrong thing.
Note that '10' > 9 is true — one string and one number means the string is
converted. So the bug only appears when both sides are text, which is precisely
when you are least likely to notice.
Convert at the boundary. The moment a value arrives from a form, a URL or an API, turn it into the type you want and check it worked:
const raw = '10';
const plates = Number(raw);
if (Number.isNaN(plates)) {
console.log('Please enter a number.');
} else {
console.log(plates + 5);
}
15
| Converting to | Use | Note |
|---|---|---|
| Number | Number(x) |
'' becomes 0. Whole string must be numeric. |
| Number, loosely | parseInt(x, 10) |
parseInt('12abc', 10) is 12. Always pass the 10. |
| Decimal | parseFloat(x) |
|
| String | String(x) or `${x}` |
|
| Boolean | Boolean(x) |
Next lesson. |
NaN, the value that is not equal to itself
NaN means "Not a Number", and you get it when a numeric conversion fails.
console.log(Number('12abc'));
console.log(typeof NaN);
console.log(NaN === NaN);
NaN
number
false
Three oddities in three lines. typeof NaN is 'number' — it is a number-typed
value representing a failed number. And NaN is not equal to itself, which
means if (x === NaN) is never true and is always a bug.
Test with Number.isNaN(x). Not the older global isNaN(), which coerces first
and so claims isNaN('hello') is true for the wrong reason.
== versus ===, settled
=== compares without converting. == converts first, using rules almost nobody
has memorised.
console.log('0' == 0);
console.log('' == 0);
console.log('' == '0');
true
true
false
Read those three again. '' equals 0, and '0' equals 0, but '' does not
equal '0'. Equality that is not transitive is not equality in any useful sense,
and it is why the argument about == is over.
Use === and !== everywhere. There is one accepted exception:
console.log(null == undefined);
console.log(null === undefined);
true
false
value == null is a deliberate, idiomatic way to ask "is this null or
undefined", and you will see it in professional code. Everything else uses ===.
Check your work
'5' + 2 is '52'; '5' - 2 is 3. + joins when either side is text;
- has no text meaning, so both become numbers.
1 + 2 + '3' is '33' and 1 + '2' + 3 is '123'. Left to right. The
first does arithmetic then joins; the second joins and never stops.
typeof null is 'object'. A 1995 bug preserved for compatibility. Test
with value === null.
typeof [] is 'object' too — use Array.isArray().
'10' > '9' is false. Two strings compare alphabetically, and '1'
precedes '9'. 10 > 9 is true, and '10' > 9 is also true, because one
number on either side converts the other.
Number('') is 0. An empty field becomes a valid zero, not NaN — which
is why an emptiness check has to happen before the conversion, not after.
NaN === NaN is false, and typeof NaN is 'number'. Use
Number.isNaN(x).
'0' == 0 and '' == 0 are both true, but '' == '0' is false. ==
converts before comparing, and the result is not transitive. Use ===.
null == undefined is true; null === undefined is false. This is the
single accepted use of ==.
platesFromForm + 5 where the form gave '10' is '105'. Convert at the
boundary with Number() and check Number.isNaN before using the result.
Practice
- Print
typeoffor a number, a string, a boolean,undefined,null, an array and an object. Two of the seven answers are surprising — say which, and why, before you run it. - Predict then check:
'5' + 2,'5' - 2,'5' * '2',5 + true,'5' + null. Write your prediction down first; the point is the gap between prediction and result. - Work out
1 + 2 + '3' + 4 + 5by hand, left to right, then run it. - Build the form bug. Set
const plates = '10'and compute a total at ₹80. Get it wrong, then fix it withNumber(), then confirm the total is ₹800. - Run
'10' > '9'and10 > 9. Then write a stock check comparing two string quantities and watch it approve an order it should reject. - Go through the conversion table and verify every row yourself with
Number(),String()andBoolean(). Two rows will surprise you; find them. - Prove
==is not transitive with'','0'and0. Then decide, in your own words, why===is the default. - Write a
safeNumber(raw)that returns a number, ornullwhen the input is empty or not numeric. Test it with'10','',' 12 ','12abc'and'0'. Note that'0'must succeed and''must not — that is the whole difficulty, andNumber('')being0is why.
Next: strings and template literals — the type most of your data actually arrives as.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship