Chapter 4 of 28 · JavaScript for Beginners — Complete Free Tutorial
Data Types & Type Coercion
JavaScript is dynamically typed — variables have no fixed type; the values inside them do, and types can change at runtime. Knowing the seven primitive types, how typeof works, and why '5' - 1 equals 4 while '5' + 1 equals '51' saves you from an entire class of bugs.
The primitive types
| Type | Example | Description |
|---|---|---|
string | 'hello', "JS", `tpl` | Text of any length |
number | 42, 3.14, -7 | Integers and floats (one type) |
bigint | 9007199254740991n | Integers beyond Number.MAX_SAFE_INTEGER |
boolean | true, false | Logical yes/no |
undefined | let x; | Declared but no value (also missing properties) |
null | let y = null; | Intentionally empty |
symbol | Symbol('id') | Unique, immutable key (rare for beginners) |
Everything else — objects, arrays, functions, Date, RegExp — is an object (arrays are objects with numeric keys; functions are objects you can call).
typeof: asking about a value
console.log(typeof 'hi'); // → string
console.log(typeof 42); // → number
console.log(typeof true); // → boolean
console.log(typeof undefined); // → undefined
console.log(typeof null); // → object 😱 (a 1990s bug kept for compatibility)
console.log(typeof [1, 2]); // → object (use Array.isArray instead)
console.log(typeof console.log); // → function
Expected output: mostly obvious answers with two famous quirks — null and arrays report as “object”. Use Array.isArray(value) to test arrays reliably.
null vs undefined
undefined— “nobody assigned anything”: uninitialised variables, missing return values, absent object properties.null— “I deliberately cleared it”: an intentional empty state.
let middleName; // undefined
let currentUser = null; // nobody logged in (intentional)
console.log(middleName, currentUser); // → undefined null
Numbers: two gotchas
console.log(0.1 + 0.2); // → 0.30000000000000004 (floating-point IEEE 754)
console.log(0.1 + 0.2 === 0.3); // → false ❌
console.log(Number.isInteger(7)); // → true
console.log(10 / 0); // → Infinity
console.log(NaN + 1); // → NaN (Not-a-Number — a result, not a type)
Never compare floats with === directly; compare with a tolerance (Math.abs(a - b) < 0.001). And remember: typeof NaN is 'number'.
Type coercion: implicit and explicit
Explicit conversion is deliberate and clear:
String(123) // → '123'
Number('42') // → 42
Number('12abc') // → NaN
Boolean('') // → false
Boolean('hi') // → true
parseInt('42px') // → 42 (stops at first non-digit)
parseFloat('3.5m') // → 3.5
Implicit coercion happens when operators meet mixed types — and here is the classic inconsistency:
console.log('5' + 1); // → '51' (+ means string concatenation here)
console.log('5' - 1); // → 4 (- only works numerically → converts)
console.log('5' * 2); // → 10
console.log(true + 1); // → 2 (true → 1)
Expected output: the chart above is why + is dangerous with strings and why you should convert explicitly at the boundaries of your code (user input, API data) instead of hoping.
Truthy and falsy
Conditions coerce values to booleans. Exactly these are falsy:
| Falsy | Truthy (everything else) |
|---|---|
false, 0, -0, 0n | 42, -1 |
'' (empty string) | '0', 'false' 😬 |
null, undefined | [], {} (both truthy!) |
NaN | new Date() |
if (username) { … } // fails only for '' / null / undefined
if (items.length) { … } // [] is truthy — check .length, not the array
The empty-array trap trips everyone once. A related question that decides which rule wins in expressions — == vs ===:
console.log(5 == '5'); // → true (coerces first 😬)
console.log(5 === '5'); // → false (type must match too ✅)
Always use === and !== unless you have a specific reason not to.
Common mistakes
- Using
==— its coercion rules are famously confusing ('' == false→ true). - Float equality —
0.1 + 0.2 === 0.3is false forever; that is IEEE 754, not a bug in your code. - Checking
typeofagainst a variable —typeof x == someValueis wrong; compare to a string literal:typeof x === 'string'. - Trusting
'0'or'false'— non-empty strings are truthy regardless of content.
Practice checklist
- Predict, then run:
'10' + '5','10' - '5',NaN == NaN,[] == false. - Write a
describe(value)function returning the type name — handle the array and null quirks. - Convert the string
'25'to a number safely, guarding againstNaN.
Values are useful only when you can combine them — next: Operators & Expressions.
Frequently Asked Questions
JavaScript is the programming language of the web. It adds behaviour to pages: responding to clicks and input, validating forms, fetching data from APIs, updating the DOM without reloading, and much more. It also runs on servers (Node.js), in desktop and mobile apps, and in build tooling.
They are unrelated languages that share a similar name for historical marketing reasons. Java is a statically-typed compiled language; JavaScript is a dynamically-typed interpreted language designed for the browser. Knowing one does not imply knowledge of the other.
Helpful but not mandatory — HTML gives you the structure JavaScript manipulates and CSS the styling it toggles. If you know basic tags, classes and selectors, you have enough to start; the DOM chapters assume only that much.
Use const by default and let when the value must change. Avoid var entirely in modern code: it is function-scoped instead of block-scoped, hoists differently, and can create accidental globals. This course uses const and let throughout.
The Document Object Model is the browser’s in-memory tree of objects representing your HTML. JavaScript reads and modifies that tree — selecting elements, changing text, adding nodes, attaching event listeners — and the browser re-renders the page to match.
== compares after converting types (so 5 == "5" is true), while === requires the same value AND the same type (5 === "5" is false). Loose equality has famously confusing rules — use === and !== everywhere unless you have a specific reason not to.
JavaScript is single-threaded, so long-running work (timers, fetches, events) is handled by the browser and its callbacks are queued. The event loop moves queued callbacks onto the call stack only when it is empty, with promise callbacks (microtasks) taking priority over timers. It is why asynchronous code never freezes the page.
A Promise is an object representing a future value — pending, fulfilled or rejected — that can be chained and combined (Promise.all). async/await is syntax sugar over promises that lets asynchronous code read top-to-bottom with ordinary try/catch error handling.
Yes. Frameworks are built on exactly these fundamentals — components are functions, state is your variable discipline, effects are events, rendering is DOM work. Developers who learn vanilla first understand what the framework is doing and debug far faster.
Type out the examples instead of copying, build small end-to-end projects (expense tracker, weather app, quiz), handle every UI state (loading, empty, success, error), and revisit fundamentals while reading real code. Consistency beats volume — a little most days outpaces occasional marathons.