EMFAD Courses

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:

TypeExamplestypeof result
string"Nairobi", 'KES'"string"
number42, 3.14, NaN, Infinity"number"
bigint9007199254740993n"bigint"
booleantrue, false"boolean"
undefinedundefined"undefined"
symbolSymbol("id")"symbol"
nullnull"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 digits

The 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 objects

A 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;                              // 5

A 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

Enjoyed this lesson?

The full course has 15 lessons with exercises, a quiz after every module, a final project and a certificate of completion. About 6 hours, USD 39, lifetime access.

Course details