JavaScript Data Types

7 minute read

Every value in JavaScript has a type — and the language has exactly eight of them. Seven are primitives (immutable, compared by value), and one is object (mutable, compared by reference). Knowing this short list cold makes the rest of the language click into place.

The Eight Types

typeof "hello"          // "string"
typeof 42               // "number"
typeof 9007199254740993n // "bigint"
typeof true             // "boolean"
typeof undefined        // "undefined"
typeof null             // "object"   ← famous historical bug
typeof Symbol("id")     // "symbol"
typeof { a: 1 }         // "object"
# Type Category Example
1 string primitive "hello", 'a', `hi`
2 number primitive 42, 3.14, NaN, Infinity
3 bigint primitive 9007199254740993n
4 boolean primitive true, false
5 undefined primitive undefined
6 null primitive null
7 symbol primitive Symbol("id")
8 object reference {}, [], function(){}, new Date()

That’s it — there isn’t a ninth type hiding anywhere.

Primitives vs Objects

Primitives are immutable and compared by value. Objects are mutable and compared by reference.

// Primitive comparison
"hi" === "hi"           // true — same value
let a = 5, b = 5;
a === b                 // true

// Object comparison
{ a: 1 } === { a: 1 }   // false — different objects in memory
let x = { a: 1 };
let y = x;
x === y                 // true — same reference
PRIMITIVE                      OBJECT
let a = 5         ┌───┐        let x = {a:1}      ┌───────┐
let b = a         │ 5 │  ◄─a   let y = x          │ {a:1} │  ◄─x, y
                  └───┘  ◄─b                      └───────┘
b = 99 doesn't                  y.a = 99 changes
affect a                        what x.a sees too
let a = 5;
let b = a;
b = 99;
console.log(a, b);   // 5 99      — primitives are copied

let x = { v: 5 };
let y = x;
y.v = 99;
console.log(x.v);    // 99        — same object via two names

The typeof Operator

typeof returns a string describing the operand’s type:

typeof "hi"          // "string"
typeof 42            // "number"
typeof true          // "boolean"
typeof undefined     // "undefined"
typeof null          // "object"     ← historical bug, never fixed
typeof []            // "object"     ← arrays are objects
typeof function(){}  // "function"   ← functions are objects too, but get their own label
typeof NaN           // "number"     ← yes, "Not a Number" is a number
typeof 10n           // "bigint"
typeof Symbol()      // "symbol"
Quirk Why
typeof null === "object" Bug from 1995 — fixing it would break the web
typeof NaN === "number" NaN is a numeric value meaning “invalid number”
typeof function(){} === "function" Spec exception — functions are technically objects
typeof [] === "object" Arrays are objects under the hood

To distinguish arrays from plain objects, use Array.isArray():

Array.isArray([1, 2, 3]);    // true
Array.isArray({ a: 1 });     // false

String

A string is a sequence of characters. Use double quotes, single quotes, or backticks — backticks unlock interpolation and multi-line.

const a = "double";
const b = 'single';
const c = `backtick: ${1 + 1}`;   // "backtick: 2"
const d = `multi
            line`;

Strings are immutable — methods that look like they modify a string actually return a new one:

const s = "hello";
s.toUpperCase();   // "HELLO" — new string
console.log(s);    // "hello"  — original unchanged

Full string coverage in chapters 14–15.

Number

JavaScript has one numeric type: a 64-bit floating point (IEEE 754). No int vs float distinction.

1
1.5
-3
1e6              // 1,000,000
1_000_000        // numeric separator for readability
0xff             // hex
0b1010           // binary
0o755            // octal

Infinity
-Infinity
NaN              // "Not a Number" — result of bad math

Floating point has the usual surprises:

0.1 + 0.2;         // 0.30000000000000004
0.1 + 0.2 === 0.3; // false

Numbers are safe up to Number.MAX_SAFE_INTEGER (≈ 9 quadrillion). Beyond that, use BigInt. Numbers get their own chapters 16–17.

BigInt

For integers larger than 2^53. Append n to a literal, or call BigInt():

const big = 9007199254740993n;
const bigger = big * 100n;

big + 1n;           // 9007199254740994n
big + 1;            // ❌ TypeError — can't mix BigInt and Number
Number(big);        // 9007199254740992 — loses precision!

You rarely need BigInt unless you’re working with high-precision math, crypto, or 64-bit IDs.

Boolean

Just two values:

true
false

Produced by comparisons:

5 > 3        // true
"a" === "b"  // false

undefined vs null

Two ways to say “no value”, with different intents:

  undefined null
Meaning “Hasn’t been assigned” “Explicitly empty”
Set by The language (default) You (the programmer)
typeof "undefined" "object" (historical bug)
In JSON Not allowed (omitted) Allowed
let x;                 // undefined — never assigned
const y = null;        // null — intentionally empty

function greet(name) {
  console.log(name);   // undefined if you don't pass an argument
}
greet();
null == undefined    // true  — loose equality treats them as equal
null === undefined   // false — strict equality is strict about types

Tip: Use == to check for “either null or undefined” with one comparison: if (value == null) { … }. It’s the one place loose equality is idiomatic.

Symbol

A unique, opaque value. Mainly used as collision-proof object keys.

const id = Symbol("id");
const id2 = Symbol("id");

id === id2;   // false — every Symbol is unique, even with the same description

const obj = { [id]: 123 };
console.log(obj[id]);   // 123

You’ll rarely create symbols yourself — but you’ll meet built-in ones like Symbol.iterator (chapter 49).

Object — Everything Else

If it’s not a primitive, it’s an object. Objects, arrays, functions, classes, Date, Map, Set, regex — all objects.

const obj   = { a: 1, b: 2 };       // plain object
const arr   = [1, 2, 3];            // array
const fn    = () => "hi";           // function
const cls   = new Date();           // class instance
const map   = new Map();
const re    = /^[a-z]+$/i;

Objects are reference types — assignment copies the reference, not the data. We’ll cover them in chapters 20–21.

Type Coercion

JavaScript converts types automatically when operators demand it. This is convenient but bug-prone.

"5" + 3        // "53"   — + with a string concatenates
"5" - 3        // 2      — - forces numeric conversion
5 + true       // 6      — true → 1
"5" == 5       // true   — loose equality coerces
"5" === 5      // false  — strict equality doesn't
!!"hello"      // true   — !! converts to boolean
+"42"          // 42     — unary + converts to number

Five values are falsy — they coerce to false in a boolean context:

false   0   ""   null   undefined   NaN

Everything else is truthy — including "0", "false", [], and {} (yes, an empty array is truthy!).

if ("0")  console.log("truthy");   // logs "truthy" — non-empty string
if ([])   console.log("truthy");   // logs "truthy" — any object
if ({})   console.log("truthy");   // logs "truthy" — any object
if (0)    console.log("no");       // doesn't log
if ("")   console.log("no");       // doesn't log
if (null) console.log("no");       // doesn't log
Value Coerces to boolean
false, 0, -0, 0n, "", null, undefined, NaN false
Everything else true

Warning: Prefer strict equality (===) and explicit conversion (Number(x), String(x), Boolean(x)) over implicit coercion. It saves debugging time.

Explicit Conversion

When you genuinely need to convert types, be explicit:

Number("42")       // 42
Number("42px")     // NaN
parseInt("42px")   // 42       — parses leading digits
parseFloat("3.14") // 3.14

String(42)         // "42"
(42).toString()    // "42"

Boolean(0)         // false
Boolean("hello")   // true
!!"hello"          // true     — shorthand

A Realistic Example

function summariseUser(user) {
  // Validate types defensively at boundaries
  if (typeof user !== "object" || user === null) {
    throw new TypeError("user must be an object");
  }

  const name = String(user.name ?? "anonymous");          // string
  const age = Number.isFinite(user.age) ? user.age : 0;   // number
  const isAdmin = Boolean(user.isAdmin);                  // boolean
  const id = user.id ?? null;                             // null fallback

  return { name, age, isAdmin, id };
}

Explicit conversions, strict checks, and ?? (nullish coalescing) instead of || keep this code predictable.

Best Practices Checklist

  • ✅ Know the eight types cold — string, number, bigint, boolean, undefined, null, symbol, object
  • ✅ Use === and !==, not == and != (except x == null)
  • ✅ Convert types explicitly (Number(x), String(x))
  • ✅ Use Array.isArray() to test for arrays
  • ✅ Default to ?? over || when checking for null/undefined
  • ❌ Don’t rely on implicit coercion in conditions
  • ❌ Don’t use typeof to test for null (it returns "object")
  • ❌ Don’t compare floating-point numbers for exact equality

What’s Next?

You know what kinds of values exist. Next, let’s learn the operators that combine and compare them — arithmetic, logical, comparison, and the modern goodies like ?? and ?..

← Previous: JS Const Next: JS Operators →

Updated: