Data types

Every value you touch in JavaScript carries a type — a label that tells the engine what kind of thing it is holding and what it is allowed to do with it. Add two numbers and you get arithmetic; add two strings and you get glue. Same +, completely different behaviour, decided entirely by type.

JavaScript has eight basic types. This chapter is the map: we name all eight and give each a sentence or two, then later chapters zoom into the ones that carry real weight. Learn the shape of the whole territory first — the details land far more easily once you know where they belong.

Here is the first surprising thing. A variable in JavaScript is not welded to a type. The same variable can hold a string now and a number a moment later, and the engine won’t complain:

// no error
let message = "hello";
message = 123456;

Languages that allow this are called dynamically typed: the values have types, but the variables that hold them do not. A box that happily accepts whatever you drop in it.

Number

let n = 123;
n = 12.345;

The number type covers both integers and floating-point numbers — there is no separate int and float here. 123 and 12.345 are the same type.

All the everyday operations live here: multiplication *, division /, addition +, subtraction -, and more. We’ll spend a whole chapter on their quirks later in Numbers.

Beyond ordinary numbers, this type includes three special numeric values: Infinity, -Infinity, and NaN. They look exotic, but you’ll meet them by accident long before you reach for them on purpose.

  • Infinity represents the mathematical Infinity ∞ — a value greater than any number.

    You get it from dividing by zero:

    alert( 1 / 0 ); // Infinity

    Or by naming it directly:

    alert( Infinity ); // Infinity
  • NaN means “Not a Number” — the result of a maths operation that made no sense:

    alert( "not a number" / 2 ); // NaN, such division is erroneous

    NaN is sticky. Once it appears, it contaminates every operation it touches:

    alert( NaN + 1 ); // NaN
    alert( 3 * NaN ); // NaN
    alert( "not a number" / 2 - 1 ); // NaN

    So a single NaN buried deep in a long calculation poisons the entire result. (There is exactly one exception: NaN ** 0 is 1.) When a computed value comes out as NaN, treat it as a breadcrumb — somewhere upstream, a non-number slipped into the maths.

Those special values formally belong to the number type, even though NaN and Infinity aren’t numbers in the everyday sense. More on all of this in Numbers.

BigInt

The number type can’t safely hold integers larger than (253-1) — that’s 9007199254740991 — or smaller than -(253-1).

To be precise, it can store bigger values (up to about 1.7976931348623157 * 10308), but past the safe range they lose precision, because a 64-bit slot only has room for so many digits. The result is an approximation. You can watch it happen:

console.log(9007199254740991 + 1); // 9007199254740992
console.log(9007199254740991 + 2); // 9007199254740992

Add one, add two — same answer. Past the safe range, odd integers literally can’t be represented; the number type rounds to the nearest value it can hold. For most work the safe range is plenty, but cryptography and microsecond timestamps need every digit to be exact.

That’s what BigInt is for — integers of arbitrary length. You create one by appending n to an integer literal:

// the "n" at the end means it's a BigInt
const bigInt = 1234567890123456789012345678901234567890n;

BigInt is a specialist tool, so we leave the details to its own chapter, BigInt. Reach for it when — and only when — ordinary numbers start lying to you.

String

A string is text, always wrapped in quotes:

let str = "Hello";
let str2 = 'Single quotes are ok too';
let phrase = `can embed another ${str}`;

JavaScript gives you three kinds of quotes:

  1. Double quotes: "Hello".
  2. Single quotes: 'Hello'.
  3. Backticks: `Hello`.

Double and single quotes are interchangeable “simple” quotes — pick a style and stay consistent. Backticks are the interesting ones. They’re “extended functionality” quotes: wrap an expression in ${…} inside backticks and its value is spliced right into the text.

let name = "Maya";

// embed a variable
alert( `Hello, ${name}!` ); // Hello, Maya!

// embed an expression
alert( `the result is ${1 + 2}` ); // the result is 3

Whatever sits inside ${…} is evaluated first, and its result becomes part of the string — a variable, some arithmetic, a function call, anything. This one feature replaces a mountain of clumsy "Hello, " + name + "!" concatenation.

It only works with backticks. The other quotes take ${…} literally:

alert( "the result is ${1 + 2}" ); // the result is ${1 + 2} (double quotes do nothing)

Full details live in Strings.

Boolean (logical type)

The boolean type has exactly two values: true and false. It’s the type of yes/no, on/off, is this allowed.

let nameFieldChecked = true; // yes, name field is checked
let ageFieldChecked = false; // no, age field is not checked

You’ll rarely type true/false by hand. More often they arrive as the answer to a comparison:

let isGreater = 4 > 1;

alert( isGreater ); // true (the comparison result is "yes")

4 > 1 is a question — “is four greater than one?” — and the answer is a boolean. Every if, every loop condition, every && runs on these. We’ll dig deeper in Logical operators.

The “null” value

null is its own type, with exactly one value: null itself.

let age = null;

Coming from other languages, drop your assumptions: null here is not a null pointer or a reference to a missing object. It’s a plain value meaning “nothing”, “empty”, or “value unknown”. Writing age = null is you saying, deliberately, “we don’t know the age yet.”

The “undefined” value

undefined is also a type of its own with a single value, and it sits right next to null — but the two mean different things.

undefined means “a value was never assigned”. Declare a variable without giving it a value, and that’s what you get:

let age;

alert(age); // shows "undefined"

You can assign undefined yourself:

let age = 100;

// change the value to undefined
age = undefined;

alert(age); // "undefined"

…but don’t. The convention is: let undefined be the engine’s way of saying “nothing here yet”, and use null when you want to say “intentionally empty”. Two words, two jobs — keep them apart and your code stays readable.

Objects and Symbols

Seven of the eight types are primitive — each value holds one single thing: a number, a string, a boolean. The eighth, object, is different in kind: it holds collections — many named values bundled together, and structures far more complex than any primitive.

let user = { name: "Maya", age: 30 };

Objects are the backbone of real JavaScript, so they get their own chapter, Objects — after we’ve finished with primitives, because objects are easier to understand once the simple pieces are familiar.

The symbol type creates unique identifiers, mostly used as special object keys. We name it here for completeness and postpone it until objects make it relevant.

number
bigint
string
boolean
null
undefined
symbol
object
The eight types: seven primitives that each hold one value, and object, which holds collections.

The typeof operator

When you need to ask a value what it is, use typeof. It returns a string naming the type — handy for quick checks and for code that must treat different types differently.

typeof undefined // "undefined"

typeof 0 // "number"

typeof 10n // "bigint"

typeof true // "boolean"

typeof "foo" // "string"

typeof Symbol("id") // "symbol"

typeof Math // "object"  (1)

typeof null // "object"  (2)

typeof alert // "function"  (3)

The last three deserve a note:

  1. Math is a built-in object full of mathematical helpers (covered in Numbers). Here it’s just a stand-in for “some object”.
  2. typeof null returns "object" — and that is a bug in the language, baked in since JavaScript’s earliest days and frozen for backwards compatibility. null is not an object. Don’t trust typeof on this one; test value === null directly.
  3. typeof alert returns "function", even though there’s no separate “function” type — functions are objects under the hood. typeof reports "function" anyway because in practice it’s genuinely useful to know “can I call this?”

See it for yourself

Types feel abstract until you watch one value become another. Type any value below — or click a preset — and see what String(), Number(), and Boolean() make of it. Try the tricky ones: "", "0", null, []. Notice how "0" is a truthy string but converts to the number 0.

Try it — type conversion
value =string
String("0")
"0"

String() wraps the value in quotes-worth of text — almost anything converts cleanly.

Number("0")
0

Number() reads a numeric value. Empty/whitespace → 0, non-numeric text → NaN, true → 1, null → 0, undefined → NaN.

Boolean("0")
true

Boolean() asks "is this truthy?" Only 0, "", null, undefined, NaN and false are falsy — everything else is true.

The rules driving that widget — when and why JavaScript converts between types — are the whole subject of the next chapter, Type Conversions.

Summary

JavaScript has 8 basic data types.

  • Seven primitive types, each holding a single value:
    • number — integers and floats alike, safe up to ±(253-1).
    • bigint — integers of arbitrary length.
    • string — text; zero or more characters, with no separate single-character type.
    • booleantrue / false.
    • null — a type of its own whose only value, null, means “empty” or “unknown”.
    • undefined — a type of its own whose only value, undefined, means “not assigned”.
    • symbol — unique identifiers.
  • One non-primitive type:
    • object — collections and complex structures.

The typeof operator reports which type a value has:

  • Written typeof x (or typeof(x)).
  • Returns a string like "string".
  • Returns "object" for null — a long-standing language bug, not the truth.

Next we focus on the primitives one by one. Once they’re second nature, objects open up easily. First stop: how JavaScript quietly converts one type into another.