JavaScript Data Types: The Complete Deep Dive
Every value in JavaScript has a type. Understanding the type system is fundamental because it determines what operations you can perform, how comparisons work, and where bugs hide. JavaScript has eight data types — seven primitives and one non-primitive. Let us master all of them.
The seven primitive types
Primitives are the simplest building blocks. They are immutable (cannot be changed) and compared by value (two primitives are equal if their values are the same).
1. String
A sequence of characters. Strings are the most common data type in web development.
// Three ways to create strings
const single = 'Hello'; // single quotes
const double = "Hello"; // double quotes (identical to single)
const template = `Hello`; // template literal (backticks) — the most powerful
// Template literals allow embedded expressions
const name = 'Ada';
const greeting = `Hello, ${name}! Today is ${new Date().toLocaleDateString()}.`;
// Multi-line strings (only with backticks)
const html = `
<div class="card">
<h2>${name}</h2>
<p>Welcome back!</p>
</div>
`;
// String properties and methods
'hello'.length // 5
'hello'.toUpperCase() // 'HELLO'
'hello'.includes('ell') // true
'hello'.startsWith('he') // true
'hello'.indexOf('l') // 2
'hello'.slice(1, 4) // 'ell'
'hello'.replace('l', 'L') // 'heLlo' (first match only)
'hello'.replaceAll('l', 'L') // 'heLLo' (all matches)
' hello '.trim() // 'hello'
'hello'.split('') // ['h', 'e', 'l', 'l', 'o']
'a,b,c'.split(',') // ['a', 'b', 'c']
'hello'.repeat(3) // 'hellohellohello'
'hello'.padStart(10, '0') // '00000hello'
'hello'.at(-1) // 'o' (last character)
// Strings are IMMUTABLE
let str = 'hello';
str[0] = 'H'; // Does nothing! No error, but no change.
str = 'Hello'; // You must create a new string
2. Number
JavaScript has one number type for both integers and floating-point numbers. It uses 64-bit IEEE 754 floating-point format.
// All of these are the same type: "number"
const integer = 42;
const float = 3.14;
const negative = -7;
const scientific = 2.998e8; // 299,800,000
const hex = 0xFF; // 255
const binary = 0b1010; // 10
const octal = 0o777; // 511
const separator = 1_000_000; // 1000000 (underscores for readability)
// Special numeric values
Infinity // result of 1/0
-Infinity // result of -1/0
NaN // "Not a Number" — result of invalid math
// The floating-point trap
0.1 + 0.2 // 0.30000000000000004 (NOT 0.3!)
0.1 + 0.2 === 0.3 // false!
// Solutions to floating-point problems:
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON // true (epsilon comparison)
Math.round((0.1 + 0.2) * 100) / 100 // 0.3 (round to cents)
// For money: use integers (cents) and divide for display
// Number methods
Number.isInteger(42) // true
Number.isInteger(42.0) // true
Number.isInteger(42.5) // false
Number.isFinite(42) // true
Number.isFinite(Infinity) // false
Number.isNaN(NaN) // true
Number.isNaN('hello') // false (use this, not global isNaN)
// Parsing strings to numbers
Number('42') // 42
Number('42.5') // 42.5
Number('') // 0
Number('hello') // NaN
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
parseInt('42px') // 42 (stops at first non-digit)
parseFloat('3.14em') // 3.14
parseInt('0xFF', 16) // 255 (second argument is the radix)
+'42' // 42 (unary plus — shortest conversion)
// Safe integer range
Number.MAX_SAFE_INTEGER // 9007199254740991 (2^53 - 1)
Number.MIN_SAFE_INTEGER // -9007199254740991
// Beyond this range, integers lose precision!
9007199254740992 === 9007199254740993 // true! (both round to same value)
// Math object
Math.round(4.5) // 5
Math.floor(4.9) // 4
Math.ceil(4.1) // 5
Math.trunc(4.9) // 4 (removes decimal, no rounding)
Math.abs(-7) // 7
Math.max(1, 5, 3) // 5
Math.min(1, 5, 3) // 1
Math.pow(2, 10) // 1024
Math.sqrt(144) // 12
Math.random() // random number between 0 (inclusive) and 1 (exclusive)
// Random integer between min and max (inclusive)
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
randomInt(1, 6) // simulates a dice roll
3. BigInt
For integers larger than Number.MAX_SAFE_INTEGER. Created by appending n to a number:
const big = 9007199254740993n; // note the 'n' suffix
const alsobig = BigInt('9007199254740993');
big + 1n // 9007199254740994n (correct!)
// big + 1 // TypeError: Cannot mix BigInt and other types
// Use BigInt for: cryptocurrency amounts, database IDs, large calculations
4. Boolean
Only two values: true and false.
const isActive = true;
const isDeleted = false;
// Boolean conversion — understanding "truthy" and "falsy"
// These are the ONLY falsy values (everything else is truthy):
Boolean(false) // false
Boolean(0) // false
Boolean(-0) // false
Boolean(0n) // false (BigInt zero)
Boolean('') // false (empty string)
Boolean(null) // false
Boolean(undefined) // false
Boolean(NaN) // false
// Common truthy surprises:
Boolean('0') // true! (non-empty string)
Boolean('false') // true! (non-empty string)
Boolean([]) // true! (empty array is an object)
Boolean({}) // true! (empty object)
Boolean(-1) // true! (any non-zero number)
Boolean(' ') // true! (space is not empty)
// This matters for conditionals:
const username = '';
if (username) {
console.log('logged in'); // skipped because '' is falsy
} else {
console.log('please log in'); // this runs
}
5. null
Represents an intentional absence of value. You set a variable to null to say "this has no value on purpose."
let user = null; // user exists but has no value yet
user = { name: 'Ada' }; // now it has a value
user = null; // explicitly cleared
typeof null // 'object' — this is a famous bug from 1995, never fixed
6. undefined
Represents a variable that has been declared but not assigned, or a missing property/argument.
let x;
console.log(x); // undefined (declared but no value assigned)
const obj = { a: 1 };
console.log(obj.b); // undefined (property does not exist)
function greet(name) {
console.log(name); // undefined if called without arguments
}
greet();
// null vs undefined: use null when YOU clear something,
// and let undefined mean "not set yet" or "missing"
7. Symbol
Unique, immutable identifiers. Primarily used for object property keys that will never collide with other keys:
const id = Symbol('user-id');
const anotherId = Symbol('user-id');
id === anotherId // false! Every Symbol is unique
// Use case: hidden object properties
const SECRET_KEY = Symbol('secret');
const user = {
name: 'Ada',
[SECRET_KEY]: 'classified-data'
};
console.log(user.name); // 'Ada'
console.log(user[SECRET_KEY]); // 'classified-data'
// Object.keys(user) // ['name'] — symbol keys are hidden!
// Well-known symbols (advanced): Symbol.iterator, Symbol.toPrimitive, etc.
// These let you customize how objects behave with built-in operations.
The non-primitive: Object
Everything that is not a primitive is an object. Objects are collections of key-value pairs. Arrays, functions, dates, regex, maps, sets — they are all objects underneath.
// Plain object
const user = {
name: 'Ada',
age: 30,
isActive: true,
address: {
city: 'London',
country: 'UK'
}
};
// Array (a special kind of object)
const numbers = [1, 2, 3, 4, 5];
typeof numbers // 'object'
Array.isArray(numbers) // true (the proper way to check)
// Function (also an object!)
function greet() { return 'hello'; }
typeof greet // 'function' (special typeof case)
greet.customProp = 'functions are objects!';
// Date
const now = new Date();
typeof now // 'object'
// RegExp
const pattern = /hello/gi;
typeof pattern // 'object'
Primitives vs objects: the critical difference
// Primitives are compared by VALUE
const a = 'hello';
const b = 'hello';
a === b // true (same characters = same value)
// Objects are compared by REFERENCE (memory address)
const obj1 = { name: 'Ada' };
const obj2 = { name: 'Ada' };
obj1 === obj2 // false! Different objects in memory, even with same contents.
const obj3 = obj1; // obj3 points to the SAME object as obj1
obj3 === obj1 // true — same reference
obj3.name = 'Bob'; // modifying obj3 also changes obj1!
console.log(obj1.name); // 'Bob' — because they are the same object
The typeof operator
typeof returns a string indicating the type of a value:
typeof 'hello' // 'string'
typeof 42 // 'number'
typeof 42n // 'bigint'
typeof true // 'boolean'
typeof undefined // 'undefined'
typeof Symbol() // 'symbol'
typeof null // 'object' ← BUG (should be 'null')
typeof {} // 'object'
typeof [] // 'object' ← arrays are objects
typeof function(){} // 'function' ← special case
// Better type checking
Array.isArray([]) // true
value === null // check for null specifically
value instanceof Date // check for Date
Object.prototype.toString.call(value) // '[object Array]', '[object Date]', etc.
Type conversion (explicit)
You can convert between types explicitly:
// To String
String(42) // '42'
String(true) // 'true'
String(null) // 'null'
String(undefined) // 'undefined'
(42).toString() // '42'
(255).toString(16) // 'ff' (hex)
(10).toString(2) // '1010' (binary)
// To Number
Number('42') // 42
Number(true) // 1
Number(false) // 0
Number(null) // 0
Number(undefined) // NaN
Number('') // 0
Number('hello') // NaN
+'42' // 42 (shorthand)
// To Boolean
Boolean(0) // false
Boolean('') // false
Boolean(null) // false
Boolean('hello') // true
Boolean(42) // true
Boolean([]) // true
!!value // shorthand for Boolean(value)
Checking for emptiness
A common need — here is how to check if different types are "empty":
// String
str === '' // empty string
str.trim() === '' // empty or whitespace only
// Array
arr.length === 0 // empty array
// Object
Object.keys(obj).length === 0 // empty object (no own properties)
// Null or undefined
value == null // checks for both null AND undefined
value === null // only null
value === undefined // only undefined
// Any falsy value
if (!value) { /* value is falsy */ }
// Nullish (only null or undefined, not 0 or '')
value ?? 'default' // uses 'default' only if value is null/undefined
Practical patterns
Default values
// Old way (broken for falsy values like 0 or '')
function greet(name) {
name = name || 'Guest'; // BUG: greet('') gives 'Guest'
}
// Modern way: nullish coalescing
function greet(name) {
name = name ?? 'Guest'; // Only replaces null/undefined
}
// Best way: default parameters
function greet(name = 'Guest') {
console.log(`Hello, ${name}`);
}
Type-safe comparisons
// ALWAYS use === (strict equality)
42 === '42' // false (different types)
42 == '42' // true (coercion! dangerous)
null === undefined // false
null == undefined // true (the ONE acceptable use of ==)
// Use == null to check for both null and undefined:
if (value == null) {
// value is null OR undefined
}
What to learn next
You now understand every data type in JavaScript, how they differ, and the critical distinction between primitives and objects. Your next topics:
- Type coercion — how JavaScript automatically converts types and how to avoid bugs from it.
- Strings & template literals — deep dive into string manipulation.
- Operators — all the operators JavaScript provides.