Historical Context & Motivation
Every programming language needs a way to represent different kinds of data—numbers for math, text for messages, true/false values for decisions. When Brendan Eich created JavaScript in just ten days in 1995, he designed a small but powerful set of data types that would let web pages come alive with interactivity. Unlike languages such as Java or C++, JavaScript was designed to be dynamically typed, meaning a variable could hold a number one moment and a string the next. This flexibility made JavaScript approachable for beginners, but it also meant that understanding types became even more important for avoiding tricky bugs.
number, string, boolean, null, undefined, and object as the core types of the language.Symbol as a new primitive type, along with let and const for better variable declarations. The original six types remain foundational.BigInt for arbitrarily large integers. Despite newer additions, the original six types still form the backbone of virtually all JavaScript code.So here's the central question this lesson tackles: what exactly are JavaScript's six original data types, how do they differ from one another, and why does choosing the right type matter when you're writing code? Let's find out.
Core Principles & Definitions
A data type tells JavaScript what kind of value a variable holds and what operations you can perform on it. JavaScript divides its types into two major categories: primitive types and reference types. Primitives are simple, immutable values—once created, they cannot be changed in place. The reference type (object) is a container that can hold collections of data and more complex entities.
Primitive vs. Reference
number, string, boolean, null, undefined) store a single, immutable value directly. Objects store a reference (address) pointing to data in memory.Dynamic Typing
let x = 5 is automatically a number.The typeof Operator
typeof to check what type a value is at any time. For example, typeof 42 returns "number".Type Coercion
"5" + 3 produces "53" (a string), not 8. Understanding types helps you predict and prevent these surprises.Immutability of Primitives
Visual Overview of JavaScript Types
The diagram above divides JavaScript's type system into two halves. On the left, inside the dashed border, you see the five primitive types. Each one stores a simple, self-contained value—like a sticky note with one fact written on it. On the right sits the object type, which acts as a container that can hold many values organized by keys. Notice the key distinction at the bottom: primitives are immutable and stored by value, while objects are mutable and stored by reference. This difference has major consequences for how your code behaves when you copy or compare data.
How Each Type Works Under the Hood
Number
JavaScript uses a single number type for both integers and decimals. Internally, every number is a 64-bit floating-point value following the IEEE 754 standard. This means 5 and 5.0 are actually the same value. Numbers also include three special values: Infinity (result of dividing by zero), -Infinity, and NaN ("Not a Number"), which appears when a math operation doesn't make sense, like "hello" * 2.
String
A string is a sequence of characters enclosed in single quotes ('hello'), double quotes ("hello"), or backticks (`hello`). Backtick strings, called template literals, let you embed expressions directly using ${expression} syntax. Strings are immutable—when you call a method like .toUpperCase(), it returns a new string rather than modifying the original.
Boolean
A boolean has exactly two possible values: true or false. Booleans are the backbone of conditional logic. Every if statement, while loop, and logical operator ultimately evaluates to a boolean. JavaScript also treats certain values as "falsy" (they behave like false in conditions): 0, "", null, undefined, and NaN. Everything else is "truthy".
Null and Undefined
Both null and undefined represent "no value," but they serve different purposes. undefined is what JavaScript assigns automatically when a variable has been declared but not yet given a value. null is an intentional assignment—a programmer's way of saying "this value is deliberately empty." Think of undefined as an empty parking spot that nobody planned, while null is a reserved parking spot with a sign that reads "kept empty on purpose."
Object
An object is a collection of key-value pairs. Keys are always strings (or Symbols), and values can be any type—including other objects. Arrays ([1, 2, 3]), functions, dates, and regular expressions are all specialized objects. Unlike primitives, objects are mutable: you can add, change, or delete properties after creation. When you assign an object to a new variable, both variables point to the same data in memory, which means changes through one variable are visible through the other.
typeof null returns "object" instead of "null". This quirk has been kept for backward compatibility. When checking for null, always use strict equality: value === null.Type Classification & the typeof Operator
The typeof operator is your primary tool for inspecting types at runtime. It returns a string describing the type of its operand. The table below shows every input-output combination you need to know, including the notorious null quirk.
| Expression | typeof Result | Actual Type |
|---|---|---|
typeof 42 | "number" | number |
typeof 3.14 | "number" | number |
typeof NaN | "number" | number (special) |
typeof "hello" | "string" | string |
typeof true | "boolean" | boolean |
typeof undefined | "undefined" | undefined |
typeof null | "object" | null (bug!) |
typeof { a: 1 } | "object" | object |
typeof [1, 2] | "object" | object (array) |
typeof function(){} | "function" | object (function) |
typeof categorizes values into its possible return strings. Note the two gotchas at the bottom: NaN is classified as a number, and null is reported as "object."The flowchart above traces the path of a value through typeof. When you need to distinguish between an actual object and null, or between a plain object and an array, typeof alone won't cut it. Use value === null for null checks and Array.isArray(value) for array checks.
Worked Example — Identifying and Using Types
Let's walk through a realistic scenario. Imagine you're building a simple student profile system. You need to store a student's name, age, enrollment status, GPA, and a list of courses. Along the way, we'll identify the type of each piece of data and see how typeof confirms our choices.
let name = "Aisha"; let age = 16; let enrolled = true; let gpa = null;typeof. Notice that typeof gpa returns "object" (the historic bug), so we use gpa === null for a reliable check instead.typeof name → "string", typeof age → "number", typeof enrolled → "boolean", typeof gpa → "object", gpa === null → truelet student = { name: "Aisha", age: 16, enrolled: true, gpa: null, courses: ["Math", "CS", "English"] };student.age (a number) and student.courses (an array, which is a type of object). We can confirm the array with Array.isArray(student.courses), which returns true.student.age → 16 (number), Array.isArray(student.courses) → truestudent.gpa = 3.8;. The object is mutable, so this change happens in place. If we had copied the object to another variable, that variable would also reflect the change because objects are stored by reference.student.gpa → 3.8, typeof student.gpa → "number"Comparing the Six Types — Strengths & Pitfalls
Each type has its natural strengths and a few common pitfalls that trip up beginners. The table below summarizes the key characteristics so you can make confident decisions about which type to use for any piece of data.
| Type | Best Used For | Common Pitfall |
|---|---|---|
number | Counts, measurements, math operations, coordinates | Floating-point precision: 0.1 + 0.2 !== 0.3 due to rounding |
string | Names, messages, IDs, any textual data | Concatenation surprise: "5" + 3 yields "53" not 8 |
boolean | Flags, toggles, conditions, filter logic | Confusing truthy/falsy values—"0" (string) is truthy, but 0 (number) is falsy |
null | Intentional "no value" placeholder | typeof null returns "object"—always use === null |
undefined | Detecting uninitialized variables or missing properties | Accidentally accessing a misspelled property returns undefined without an error |
object | Grouping related data, arrays, complex structures | Reference sharing: two variables can point to the same object, causing unintended mutations |
number instead of a string, you'll lose leading zeros (like 0551234567 becoming 551234567). Always think about what operations you need to perform on the data—that usually tells you which type fits best.Connection to Advanced Type Concepts
The six types you've learned form the foundation, but modern JavaScript and its ecosystem build significantly on top of them. Understanding where these basics lead will prepare you for more powerful tools as your programming skills grow.
| What You Know Now | Where It Leads |
|---|---|
Six core types checked with typeof | TypeScript — a superset of JavaScript that adds static type annotations so errors are caught before your code even runs |
| Dynamic typing (variables can change type) | Type guards — conditional checks (using typeof, instanceof, or custom functions) that narrow a value's type within a code block |
| Objects with key-value pairs | Classes & Prototypes — object blueprints that let you create many objects with the same structure and shared methods |
| Primitive types (number, string, boolean) | Symbol & BigInt — two newer primitive types: Symbol for unique identifiers, BigInt for integers larger than 2⁵³ − 1 |
| Type coercion surprises | Strict equality (===) and linting tools like ESLint that enforce type-safe patterns and prevent coercion bugs in large codebases |
As you advance, you'll encounter TypeScript in many professional projects. TypeScript doesn't replace what you've learned—it builds directly on top of it. Every TypeScript type annotation maps back to the JavaScript types we've covered. Mastering these six types now gives you a rock-solid foundation for everything that follows.
Practice Problems
null and undefined in JavaScript. Give a real-world analogy for each.typeof expression: (a) typeof 7.5 (b) typeof "true" (c) typeof false (d) typeof null (e) typeof [1, 2, 3]let a = "10"; let b = 5; console.log(a + b); console.log(a - b);describeValue(val) that returns a string describing the type of its argument. It should handle all six types correctly, including the null bug and arrays. Write the function body using typeof, === null, and Array.isArray().let obj1 = { score: 100 }; let obj2 = obj1; obj2.score = 200; console.log(obj1.score);
What does this output and why? Now consider the same pattern with a primitive:
let x = 100; let y = x; y = 200; console.log(x);
What does this output? Explain the fundamental type system concept that causes the different behavior.Summary — JavaScript Types at a Glance
JavaScript organizes all data into primitive types and reference types. The five primitives are number (integers, decimals, NaN, Infinity), string (text in quotes or backticks), boolean (true or false), null (intentional empty value), and undefined (not yet assigned). All primitives are immutable and stored by value.
The object type is a mutable collection of key-value pairs that is stored by reference. Arrays, functions, and dates are all objects. Use typeof to check types at runtime, but remember its two quirks: typeof null returns "object" and arrays also report as "object." Guard against these with === null and Array.isArray(). Master these six types and you'll have a solid foundation for every JavaScript program you write.