Module 1: Language Core
Types, Coercion and Equality
JavaScript is famous for surprising results such as "5" + 3 giving "53" while "5" - 3 gives 2. These are not random; they follow precise rules in the language specification. Once you know the types JavaScript has and how it converts between them, you can predict these results, avoid bugs in form handling and calculations, and read other people's code with confidence.
The types
JavaScript has seven primitive types and one structural type:
| Type | Examples | typeof result |
|---|---|---|
| string | "Nairobi", 'KES' | "string" |
| number | 42, 3.14, NaN, Infinity | "number" |
| bigint | 9007199254740993n | "bigint" |
| boolean | true, false | "boolean" |
| undefined | undefined | "undefined" |
| symbol | Symbol("id") | "symbol" |
| null | null | "object" (a historical quirk) |
| object | {}, [], functions, dates | "object", or "function" for functions |
Primitives are immutable and compared by value. Objects are compared by reference: two separate objects with identical contents are not equal. To check for null, use value === null; to check for arrays, use Array.isArray(value), because typeof reports both as "object".
Coercion: implicit type conversion
When an operator receives a type it does not expect, JavaScript converts the value. The main rules:
- The
+operator: if either operand is a string (after converting objects to primitives), it concatenates. Otherwise it adds numbers. - Other arithmetic (
-,*,/,%): converts operands to numbers. - Boolean contexts (
if,&&,||,!): convert to boolean using truthiness.
"5" + 3 // "53" string wins with +
"5" - 3 // 2 - converts to number
"5" * "2" // 10
true + 1 // 2 true becomes 1
[] + [] // "" arrays become empty strings
[1, 2] + [3] // "1,23"
Number("") // 0
Number("12px") // NaN
parseInt("12px") // 12 parses the leading digitsThe falsy values are false, 0, -0, 0n, "", null, undefined and NaN. Everything else is truthy, including "0", "false", [] and {}.
Equality: ==, === and Object.is
Strict equality === compares without conversion: different types are never equal. Loose equality == converts types first using a set of rules that produce some notorious results.
0 == "" // true "" becomes 0
"0" == false // true both become 0
null == undefined // true special rule
null == 0 // false null only loosely equals undefined
NaN == NaN // false NaN is not equal to anything, even itself
NaN === NaN // false
Object.is(NaN, NaN) // true
Object.is(0, -0) // false, while 0 === -0 is true
{} === {} // false different objectsA sensible rule followed by many teams: always use === and !==, with one accepted exception, value == null, which is a concise way to check for both null and undefined. Use Number.isNaN(x) to test for NaN.
Worked example: a form total that went wrong
A checkout page reads quantities from input fields. Input values are always strings, which leads to this bug:
const qty = document.querySelector("#qty").value; // "2"
const extra = document.querySelector("#extra").value; // "3"
const total = qty + extra; // "23", not 5
// Fix: convert explicitly and validate
const q = Number(qty);
const e = Number(extra);
if (!Number.isFinite(q) || !Number.isFinite(e)) {
throw new TypeError("Quantities must be numbers");
}
const fixedTotal = q + e; // 5A second subtle bug hides in "default" logic. const discount = input || 10; intends "use 10 if nothing was entered", but if the user enters 0, it becomes 10 because 0 is falsy. The nullish coalescing operator ?? only falls back for null or undefined: const discount = input ?? 10; keeps a deliberate 0.
Number precision
JavaScript numbers are 64-bit floating point. 0.1 + 0.2 is 0.30000000000000004, and integers are only exact up to Number.MAX_SAFE_INTEGER (2 to the power 53, minus 1). For money, store whole minor units (cents) as integers, or use BigInt for very large integers. Format for display with Intl.NumberFormat, for example new Intl.NumberFormat("en-KE", { style: "currency", currency: "KES" }).format(1250).
Common mistakes to avoid
- Using == by habit. Prefer
===; a linter such as ESLint can enforce it. - Adding input values without converting. Form values are strings.
- Using || for defaults when 0 or "" are valid. Use
??. - Checking NaN with ===. Use
Number.isNaN. - Comparing objects with === and expecting a content comparison. Compare the fields you care about.
Exercise
Open your browser's developer console or a Node.js REPL and predict, before running, the result of these twelve expressions: "3" + 4 + 5, 3 + 4 + "5", "10" / "2", [] == false, null >= 0, undefined == 0, "" || "default", "" ?? "default", typeof NaN, Number(" 42 "), [] + {}, and 0.1 + 0.2 === 0.3. Run them, and for any you got wrong write one sentence explaining the rule involved.
Key points
- JavaScript has seven primitive types plus objects; typeof null is "object" by historical accident
- + concatenates if either side is a string; other arithmetic converts to numbers
- Prefer === (and value == null as a deliberate exception); use Number.isNaN for NaN
- Use ?? instead of || when 0 or empty string are valid values