JavaScript data types
The Short Answer
JavaScript has 8 data types: 7 primitives (string, number, bigint, boolean, undefined, null, symbol) and 1 non-primitive (object). Primitives are immutable, stored by value, and compared by value. Objects (including arrays, functions, dates, and everything else) are mutable, stored by reference, and compared by reference. Understanding this distinction is fundamental to avoiding bugs with equality checks, function arguments, and state management.
Primitive Types
Primitives are the simplest building blocks. They're immutable — you can't change the value itself, only reassign the variable to a new value. When you assign a primitive to another variable or pass it to a function, a copy of the value is made.
// string — text data
const name: string = 'Alice';
// number — integers and floats (64-bit IEEE 754)
const age: number = 30;
const pi: number = 3.14;
const hex: number = 0xff;
const infinity: number = Infinity;
const notANumber: number = NaN; // Still type 'number'!
// bigint — arbitrary precision integers
const huge: bigint = 9007199254740993n; // Beyond Number.MAX_SAFE_INTEGER
// boolean — true or false
const active: boolean = true;
// undefined — variable declared but not assigned
let x: undefined; // implicitly undefined
// null — intentional absence of value
const empty: null = null;
// symbol — unique, immutable identifier
const id: symbol = Symbol('userId');
const anotherId: symbol = Symbol('userId');
console.log(id === anotherId); // false — every Symbol is unique
Key details: NaN is of type number (counterintuitive but true). typeof null returns 'object' — this is a historical bug in JavaScript that can never be fixed. bigint can't be mixed with number in arithmetic without explicit conversion. Symbols are guaranteed unique, making them useful for object property keys that won't collide.
The Object Type
Everything that isn't a primitive is an object. Arrays, functions, dates, regular expressions, Maps, Sets, and plain objects are all objects under the hood. They're mutable, stored by reference, and compared by reference identity (not by content).
// All of these are objects
const obj = { name: 'Alice' }; // Plain object
const arr = [1, 2, 3]; // Array (special object)
const fn = () => 'hello'; // Function (callable object)
const date = new Date(); // Date object
const regex = /hello/gi; // RegExp object
const map = new Map(); // Map object
// Reference comparison — same content, different objects
const a = { x: 1 };
const b = { x: 1 };
console.log(a === b); // false — different references
const c = a;
console.log(a === c); // true — same reference
// Mutation through references
c.x = 99;
console.log(a.x); // 99 — a and c point to the same object
The reference behavior is the source of many bugs. When you pass an object to a function or assign it to another variable, you're sharing the same object — mutations through one reference affect all references. This is why React requires new object references for state updates (shallow comparison).
typeof and Type Checking
The typeof operator returns a string indicating the type of a value. It works well for primitives but has quirks with null and functions. For more precise type checking of objects, you need instanceof, Array.isArray(), or Object.prototype.toString.call().
| Expression | Result | Notes |
|---|---|---|
| typeof 'hello' | 'string' | |
| typeof 42 | 'number' | Includes NaN and Infinity |
| typeof 42n | 'bigint' | |
| typeof true | 'boolean' | |
| typeof undefined | 'undefined' | |
| typeof null | 'object' | ⚠️ Historical bug — null is NOT an object |
| typeof Symbol() | 'symbol' | |
| typeof {} | 'object' | |
| typeof [] | 'object' | Use Array.isArray() instead |
| typeof function(){} | 'function' | Functions get their own typeof result |
Type Coercion Gotchas
JavaScript aggressively coerces types in certain contexts — especially with == (loose equality) and arithmetic operators. This leads to famously confusing behavior. Using === (strict equality) and being explicit about conversions avoids most of these issues.
// Loose equality coercion surprises
console.log('' == false); // true (both coerce to 0)
console.log('0' == false); // true ('0' → 0, false → 0)
console.log(null == undefined); // true (special rule)
console.log(NaN == NaN); // false (NaN is never equal to anything)
// Arithmetic coercion
console.log('5' - 3); // 2 (string coerced to number)
console.log('5' + 3); // '53' (number coerced to string — concatenation!)
console.log(true + 1); // 2 (true → 1)
// Falsy values (coerce to false in boolean context)
// false, 0, -0, 0n, '', null, undefined, NaN
// Truthy surprises
console.log(Boolean('0')); // true (non-empty string)
console.log(Boolean([])); // true (empty array is truthy!)
console.log(Boolean({})); // true (empty object is truthy!)
Prefer strict equality
Always use === for comparisons. Use explicit conversions (Number(), String(), Boolean()) when you need type conversion. TypeScript catches most coercion bugs at compile time.
Why Interviewers Ask This
This is a foundational question that reveals how well you know the language. Interviewers want to hear that you can list all 8 types, explain the primitive vs object distinction (value vs reference), know the typeof quirks (null, arrays, functions), understand type coercion and how to avoid its pitfalls, and can connect this knowledge to practical concerns like React state updates and equality checks.
Quick Revision Cheat Sheet
7 primitives: string, number, bigint, boolean, undefined, null, symbol
1 non-primitive: object (includes arrays, functions, dates, maps, sets, etc.)
Primitives: Immutable, stored/compared by value, copied on assignment
Objects: Mutable, stored/compared by reference, shared on assignment
typeof null: 'object' — historical bug, use === null to check
Falsy values: false, 0, -0, 0n, '', null, undefined, NaN — everything else is truthy