JavaScript Data Types

JavaScript has 7 primitive types and 1 object type. Learn what values variables can hold, how typeof works, and why type coercion can trip you up.

Beginner 9 min read 12 examples

Data Types Overview

Every value in JavaScript has a type. There are 7 primitive types and 1 object type. Primitives are immutable values stored directly; objects are mutable collections stored by reference.

TypeExampletypeof result
String"hello""string"
Number42, 3.14"number"
Booleantrue, false"boolean"
nullnull"object" (bug)
undefinedundefined"undefined"
SymbolSymbol("id")"symbol"
BigInt9007199254740993n"bigint"
Object{}, [], functions"object" / "function"

String

Strings represent text. JavaScript strings are immutable - you cannot change a character in place; string operations always return new strings.

JavaScript
// Three ways to create strings
const single   = 'Hello, World!';
const double   = "Hello, World!";
const template = `Hello, World!`;   // template literal (backtick)

// Template literals support expressions
const name = "Alice";
const age  = 30;
console.log(`${name} is ${age} years old.`); // "Alice is 30 years old."
console.log(`2 + 2 = ${2 + 2}`);             // "2 + 2 = 4"

// Multiline with template literals
const message = `Line one
Line two
Line three`;

// String length and access
const word = "JavaScript";
console.log(word.length);  // 10
console.log(word[0]);      // "J"
console.log(word.at(-1));  // "t" (last character)

// Immutability
word[0] = "X";             // silently does nothing
console.log(word);         // "JavaScript" - unchanged

Number

JavaScript has a single number type for both integers and decimals - a 64-bit IEEE 754 floating-point value. This means very large integers lose precision (use BigInt for those).

JavaScript
const int     = 42;
const float   = 3.14159;
const negative = -7;
const exp     = 2.5e6;     // 2,500,000

// Special number values
console.log(Infinity);        // Infinity
console.log(-Infinity);       // -Infinity
console.log(0 / 0);           // NaN
console.log(1 / 0);           // Infinity

// Safe integer limit
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991 (2^53 - 1)
console.log(9007199254740991 + 1);    // 9007199254740992 - exact
console.log(9007199254740991 + 2);    // 9007199254740992 - wrong! (precision lost)

// Floating-point gotcha
console.log(0.1 + 0.2);       // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false!
// Fix: use toFixed() for display, or multiply to integers
console.log((0.1 + 0.2).toFixed(1)); // "0.3"

// Number conversion
console.log(Number("42"));    // 42
console.log(Number(""));      // 0
console.log(Number("abc"));   // NaN
console.log(parseInt("42px")); // 42
console.log(parseFloat("3.14em")); // 3.14
Floating-Point Arithmetic

The 0.1 + 0.2 !== 0.3 behavior is not a JavaScript bug - it is how IEEE 754 floating-point works in every language (Python, Java, C have the same issue). For currency or precise decimal math, use a library like decimal.js or work in integer cents.

Boolean

Booleans have only two values: true and false. Every value in JavaScript is either truthy or falsy when evaluated in a boolean context.

JavaScript
const isActive = true;
const isAdmin  = false;

// Falsy values - these are ALL the falsy values in JavaScript
console.log(Boolean(false));     // false
console.log(Boolean(0));         // false
console.log(Boolean(-0));        // false
console.log(Boolean(0n));        // false (BigInt zero)
console.log(Boolean(""));        // false (empty string)
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN));       // false

// Everything else is truthy
console.log(Boolean("0"));       // true (non-empty string)
console.log(Boolean([]));        // true (empty array)
console.log(Boolean({}));        // true (empty object)
console.log(Boolean(-1));        // true

// Boolean conversion in conditions
const name = "";
if (!name) {
    console.log("Name is empty"); // runs because "" is falsy
}

null and undefined

Both represent "no value" but with different meanings. undefined is the engine's default for an uninitialized state. null is an intentional programmer signal meaning "empty" or "no object."

JavaScript
// undefined - automatic, set by the engine
let x;
console.log(x);          // undefined (declared but not assigned)

function greet(name) {
    console.log(name);   // undefined if no argument passed
}
greet();

const obj = { a: 1 };
console.log(obj.b);      // undefined (property does not exist)

// null - intentional empty value, set by programmer
let user = null;         // "no user object yet"
user = { name: "Alice" }; // assigned later

// Checking for both (nullish check)
const val = null;
console.log(val == null);    // true (loose equality matches both null and undefined)
console.log(val === null);   // true (strict: only null)
console.log(val === undefined); // false

// null vs undefined in arithmetic
console.log(null + 1);       // 1 (null coerces to 0)
console.log(undefined + 1);  // NaN (undefined coerces to NaN)

// typeof
console.log(typeof null);       // "object" (historical bug in JS)
console.log(typeof undefined);  // "undefined"
When to Use null vs undefined

As a developer, you should: use null when you explicitly want to say "this variable intentionally has no value." Let the engine use undefined for uninitialized state - do not assign undefined yourself. A common pattern: initialize a variable to null and assign it later when the value is ready.

Symbol and BigInt

Symbol creates unique identifiers - useful for object property keys that never collide. BigInt handles integers larger than Number.MAX_SAFE_INTEGER.

JavaScript
// Symbol - always unique
const id1 = Symbol("id");
const id2 = Symbol("id");
console.log(id1 === id2);   // false - even same description, different symbol

// Symbols as object keys (avoids key collision)
const USER_ID = Symbol("userId");
const user = {
    name: "Alice",
    [USER_ID]: 42          // symbol key - hidden from for...in
};
console.log(user[USER_ID]); // 42

// BigInt - for integers beyond 2^53
const big = 9007199254740993n;  // append 'n'
const big2 = BigInt("9007199254740993");
console.log(big + 1n);           // 9007199254740994n - exact!

// Cannot mix BigInt and Number
// console.log(big + 1);         // TypeError
console.log(Number(big));         // convert BigInt to Number (may lose precision)

typeof Operator

typeof returns a string describing the type of its operand. Know the quirks - especially typeof null.

JavaScript
console.log(typeof "hello");      // "string"
console.log(typeof 42);           // "number"
console.log(typeof 3.14);         // "number"
console.log(typeof true);         // "boolean"
console.log(typeof undefined);    // "undefined"
console.log(typeof Symbol());     // "symbol"
console.log(typeof 42n);          // "bigint"

// Quirks
console.log(typeof null);         // "object" (historical bug)
console.log(typeof {});           // "object"
console.log(typeof []);           // "object" (arrays are objects)
console.log(typeof function(){}); // "function" (functions are objects too)

// Checking for null specifically
function isNull(val) {
    return val === null;
}

// Checking for an array
console.log(Array.isArray([]));   // true (use this, not typeof)

Type Coercion

Type coercion is JavaScript's automatic conversion of values from one type to another. It happens in expressions and comparisons. Understanding it prevents subtle bugs.

JavaScript
// String coercion with + operator
console.log("5" + 3);    // "53" (3 coerced to string, then concatenated)
console.log("5" + true); // "5true"
console.log("5" - 3);    // 2  (- only works on numbers, "5" coerced to 5)
console.log("5" * 2);    // 10
console.log("5" / 2);    // 2.5

// Loose equality (==) coerces before comparing - avoid!
console.log(0   == false);  // true  (false coerces to 0)
console.log("1" == 1);      // true  ("1" coerces to 1)
console.log(null == undefined); // true (special rule)
console.log(null == false);     // false (null only == undefined)

// Strict equality (===) - no coercion - always use this
console.log(0   === false); // false
console.log("1" === 1);     // false
console.log(null === undefined); // false

// Explicit conversion (better than implicit)
const str = "42";
console.log(Number(str));     // 42
console.log(parseInt(str));   // 42
console.log(+str);            // 42 (unary + operator)
console.log(String(42));      // "42"
console.log((42).toString()); // "42"
console.log(Boolean(0));      // false
Always Use === Not ==

The loose equality operator == has 49 special coercion rules that are hard to memorize and produce surprising results. Use === (strict equality) everywhere. The only acceptable use of == is checking for both null and undefined at once: value == null is true for both.

NaN - Not a Number

NaN is the result of a failed numeric operation. Despite its name it has type "number". Its strangest property: NaN !== NaN.

JavaScript
// Operations that produce NaN
console.log(0 / 0);           // NaN
console.log(parseInt("abc")); // NaN
console.log(Math.sqrt(-1));   // NaN
console.log(undefined + 1);   // NaN
console.log("abc" * 2);       // NaN

// NaN is the only value not equal to itself
console.log(NaN === NaN);     // false!
console.log(NaN == NaN);      // false!

// Correct way to check for NaN
console.log(Number.isNaN(NaN));       // true
console.log(Number.isNaN(42));        // false
console.log(Number.isNaN("NaN"));     // false (it is a string, not NaN)

// Avoid global isNaN() - it coerces first
console.log(isNaN("hello"));  // true - but "hello" is a string, not NaN
console.log(Number.isNaN("hello")); // false - correct answer

// NaN propagates through calculations
const result = NaN + 5;
console.log(result);          // NaN - any operation with NaN returns NaN

// Checking before using
function safeDivide(a, b) {
    const result = a / b;
    return Number.isNaN(result) ? 0 : result;
}

Frequently Asked Questions

undefined means a variable has been declared but not assigned a value - it is the engine's default. null is an intentional assignment meaning "no value" or "empty." Use null when you want to explicitly say a variable holds nothing. You will get undefined from uninitialized variables, missing function arguments, and missing object properties.

This is a famous bug in JavaScript that has existed since version 1. The original implementation used type tags stored in the low bits of values, and null's bit pattern matched the object tag. It was never fixed because fixing it would break millions of existing websites. Always check for null explicitly: value === null rather than relying on typeof.

NaN (Not a Number) is returned when a mathematical operation fails, like parseInt("abc") or 0/0. The tricky part is that NaN !== NaN - NaN is the only value in JavaScript not equal to itself. Use Number.isNaN(value) to check for NaN. Avoid the global isNaN() which coerces the argument first.

Type coercion is JavaScript's automatic conversion of values between types during operations. It causes problems with == (loose equality) which coerces before comparing: "5" == 5 is true. Always use === (strict equality) which does not coerce. The + operator also coerces: "5" + 3 gives "53" (string concatenation), not 8.