RizTech Academy logo
RizTech Academy
Language BasicsLesson 2 of 530 min

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

  1. Print typeof for 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.
  2. 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.
  3. Work out 1 + 2 + '3' + 4 + 5 by hand, left to right, then run it.
  4. Build the form bug. Set const plates = '10' and compute a total at ₹80. Get it wrong, then fix it with Number(), then confirm the total is ₹800.
  5. Run '10' > '9' and 10 > 9. Then write a stock check comparing two string quantities and watch it approve an order it should reject.
  6. Go through the conversion table and verify every row yourself with Number(), String() and Boolean(). Two rows will surprise you; find them.
  7. Prove == is not transitive with '', '0' and 0. Then decide, in your own words, why === is the default.
  8. Write a safeNumber(raw) that returns a number, or null when 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, and Number('') being 0 is 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