Javascript Variables, Data Types & Operators JavaScript Data Types

When I first hit a JavaScript bug where two values just would not compare correctly, I stared at the screen for a while before realizing I was comparing a string to a number. The logic was solid. The math was right. The types were wrong, and that was all it took.

If you’ve been following along with our variables in JavaScript article, you already know how to create a box for your data with let, const, and var. Now comes the natural question: what kind of data actually goes inside those boxes?

That’s exactly what this article is about. JavaScript has 8 data types in total, and understanding each one, what it does, when to use it, and the traps around it, is what separates developers who write confident code from developers who spend hours debugging what should be a simple comparison.

This isn’t going to be a dry list. We’re going to go through every type with real examples, real quirks, and a couple of interactive demos so you can actually see how this works. Let’s get into it.

🧭

This is part of our ongoing JavaScript series. If you’re jumping in fresh, I’d suggest reading Introduction to JavaScript and JavaScript Syntax Fundamentals first. They’ll give you the context that makes this article click faster.


01Before Listing the Types, Here’s Why This Actually Matters

You might be wondering: does knowing data types really prevent bugs? Short answer: yes, more than almost anything else at this stage of learning.

JavaScript is a dynamically typed language. That means you don’t declare what type a variable will hold, unlike languages like Java or C++ where you must say “this is an integer” upfront. JavaScript figures it out at runtime. That flexibility is great, but it’s also where silent bugs are born.

Here’s a real-world example. Imagine you’re reading a user’s age from an HTML form input:

// Reading from an HTML input always gives you a string
let userAge = "25";   // This is a string, not a number

// Now you do a comparison
if (userAge > 18) {
  console.log("Access granted");
}

// Works here by accident (JavaScript coerces the string to number)
// But now try this:
console.log(userAge + 5);   // "255" — not 30!

The addition gives you "255" because JavaScript saw a string and a number, and concatenated them instead of adding. This type of bug is invisible until runtime, and it can affect real users.

Knowing your types means knowing when this happens and how to prevent it.


02Two Categories That Cover Everything

Before we name all 8 types, you need to know that JavaScript groups them into just two categories:

  • Primitive types: There are 7 of them. They hold simple, single values. They’re immutable, which means the value itself cannot be changed once created.
  • The object type: Just one, but it covers a lot. Objects, arrays, functions, dates: they’re all technically this type. They’re mutable and stored differently in memory.

This distinction matters a lot later when you understand how copying works. But for now, let’s look at all 8 types in the interactive explorer below, then go through each one in detail.

Interactive Demo

JavaScript Types Explorer

Click any type card to see its details, typeof result, and example values.

Primitive
String
“hello”
Primitive
Number
42, 3.14
Primitive
BigInt
100n
Primitive
Boolean
true / false
Primitive
Undefined
undefined
Primitive
Null
null
Primitive
Symbol
Symbol(“id”)
Object
Object
{}, [], fn

👆 Click a type card above to explore it


03The 7 Primitive Types in JavaScript

Primitives are simple. Each one holds a single piece of data. They’re the atoms of JavaScript values. Let’s go through all seven.

1. String: Text Data in JavaScript

A string is any piece of text. You wrap it in single quotes, double quotes, or backticks. All three work, but backticks give you superpowers (template literals, which we’ll cover in the next article).

let firstName = "Daniyal";        // double quotes
let city = 'Karachi';             // single quotes
let message = `Hello, ${firstName}!`;  // template literal (backticks)

console.log(firstName);   // Daniyal
console.log(city);        // Karachi
console.log(message);     // Hello, Daniyal!

console.log(typeof firstName);   // "string"
console.log(firstName.length);   // 7

Strings have a length property and a ton of built-in methods like .toUpperCase(), .includes(), and .split(). We’ll cover all of those in detail in the Strings in JavaScript article coming up next in the series.

One thing worth knowing now: strings are immutable. Once you create a string value, you can’t modify individual characters in it. Every string operation returns a new string, rather than changing the original. This is important to keep in mind.

2. Number: One Type Handles All Numeric Values

In most programming languages, there are separate types for integers (whole numbers) and floats (decimals). JavaScript keeps it simple: there’s just one number type for both.

let age = 25;          // integer
let price = 9.99;      // float
let negative = -100;   // negative number
let big = 1_000_000;   // underscore separator (readable!)

console.log(typeof age);     // "number"
console.log(typeof price);   // "number" — same type!

// Special number values
console.log(Infinity);    // Infinity
console.log(-Infinity);   // -Infinity
console.log(NaN);         // NaN (Not a Number)

// NaN quirk worth knowing
console.log(typeof NaN);          // "number" — yes, NaN is type "number"
console.log("hello" / 2);         // NaN — can't divide a string
console.log(Number("hello"));     // NaN — can't convert non-numeric string
Important: JavaScript uses 64-bit floating point (IEEE 754) to store all numbers. This causes the infamous floating point issue: 0.1 + 0.2 does not equal 0.3 exactly in JavaScript. It gives 0.30000000000000004. We’ll dig into why that happens and how to handle it in the Numbers in JavaScript article coming soon.

3. BigInt: When Regular Numbers Aren’t Enough

JavaScript’s number type can safely represent integers up to 2^53 - 1, which is 9007199254740991. Once you go beyond that, you start getting rounding errors. Enter BigInt, added in ES2020.

// The limit of safe integers with regular Number
console.log(Number.MAX_SAFE_INTEGER);     // 9007199254740991

// What happens beyond the limit
console.log(9007199254740991 + 1);   // 9007199254740992 (correct)
console.log(9007199254740991 + 2);   // 9007199254740992 (wrong! same result)

// BigInt fixes this — add 'n' suffix to the number
let bigNum = 9007199254740991n;
console.log(bigNum + 1n);   // 9007199254740992n (correct!)
console.log(bigNum + 2n);   // 9007199254740993n (also correct!)

console.log(typeof bigNum);   // "bigint"

// You can't mix BigInt and Number without explicit conversion
// console.log(bigNum + 1);  // TypeError!
console.log(bigNum + BigInt(1));  // Works fine

You’ll use BigInt when working with very large integers: financial calculations, cryptography, database IDs that exceed safe integer limits. For everyday use, regular number is just fine.

4. Boolean: The Simplest Type That Powers All Your Logic

Boolean is the most straightforward type. It has exactly two possible values: true or false. That’s it. No in-between, no maybe.

let isLoggedIn = true;
let hasPermission = false;

console.log(typeof isLoggedIn);   // "boolean"

// Booleans come from comparisons
let age = 20;
console.log(age >= 18);    // true
console.log(age === 25);   // false

// Booleans drive your if statements
if (isLoggedIn) {
  console.log("Welcome back!");
} else {
  console.log("Please log in");
}

// Boolean() converts other values to boolean
console.log(Boolean(1));        // true
console.log(Boolean(0));        // false
console.log(Boolean("hello"));  // true
console.log(Boolean(""));       // false
console.log(Boolean(null));     // false

That last part, where Boolean() converts other types, is tied to a concept called truthy and falsy values, which has its own full article in our series. For now, just know that booleans are the backbone of every conditional check you write.

5. Undefined: A Variable With No Value Yet

When you declare a variable but don’t assign a value to it, JavaScript automatically gives it the value undefined. It’s also what you get when you access a function parameter that was never passed, or when a function doesn’t explicitly return anything.

let myVar;
console.log(myVar);          // undefined
console.log(typeof myVar);   // "undefined"

// Function with no return
function doSomething() {
  // no return statement
}
let result = doSomething();
console.log(result);         // undefined

// Accessing a missing property
let user = { name: "Daniyal" };
console.log(user.email);     // undefined (property doesn't exist)

Think of undefined as JavaScript’s way of saying: “this exists, but nobody gave it a value.” It’s not an error. It’s the default state of an uninitialized variable.

6. Null: The Intentional Empty Value

Null looks similar to undefined at first glance, but the intent is completely different. While undefined means “no value was assigned,” null means “I explicitly set this to empty on purpose.”

let selectedUser = null;   // intentionally empty, no user selected yet

console.log(selectedUser);          // null
console.log(typeof selectedUser);   // "object" ← famous quirk! (see note below)

// The difference in practice
let a;         // undefined — developer forgot / hasn't set it yet
let b = null;  // null — developer intentionally says "no value here"

// Comparing them
console.log(a == b);    // true  (loose equality treats them as equal)
console.log(a === b);   // false (strict equality sees they're different types)

// Checking for null or undefined
if (selectedUser === null) {
  console.log("No user selected");
}
The typeof null bug: typeof null returns "object", which is historically wrong. This is a bug from JavaScript’s very first release in 1995. It could never be fixed because too much existing code already depended on this behavior. To check if something is null, always use strict equality: value === null, not typeof value.

7. Symbol: The Guaranteed Unique Identifier

Symbol is the newest primitive type, added in ES2015. I’ll be honest: this one confused me initially too. But the concept is simple once you see it: every Symbol you create is guaranteed to be unique, no exceptions.

let sym1 = Symbol("userId");
let sym2 = Symbol("userId");

// Same description, but not equal
console.log(sym1 === sym2);    // false (always!)
console.log(typeof sym1);      // "symbol"

// Where Symbols shine: unique object keys
const ROLE_KEY = Symbol("role");

let user = {
  name: "Daniyal",
  [ROLE_KEY]: "admin"   // Symbol as a property key
};

console.log(user.name);           // "Daniyal"
console.log(user[ROLE_KEY]);      // "admin"
console.log(user["role"]);        // undefined — can't access with string key!

// Symbols don't show up in regular loops or JSON
console.log(Object.keys(user));   // ["name"] — ROLE_KEY is hidden

Symbols are most useful when you’re building libraries or frameworks and you need property keys that can’t accidentally conflict with user-defined keys. For everyday app code, you won’t use Symbols often, but knowing they exist and what they do is important.


04The 8th Type: Object, JavaScript’s One Non-Primitive

Everything that isn’t a primitive is an object. That’s a wide net. Plain objects with key-value pairs, arrays, dates, regular expressions, and even functions are all technically the object type in JavaScript.

// All of these are objects
let person = { name: "Daniyal", age: 25 };
let colors = ["red", "green", "blue"];
let today = new Date();
let pattern = /hello/gi;

console.log(typeof person);    // "object"
console.log(typeof colors);    // "object" — arrays are objects!
console.log(typeof today);     // "object"
console.log(typeof pattern);   // "object"

// Functions are objects too, but typeof gives a special result
function greet(name) {
  return `Hello, ${name}`;
}

console.log(typeof greet);    // "function" — not "object" (special case)
console.log(greet instanceof Object);   // true — still IS an object though

That last part is interesting: typeof function returns "function" even though functions are technically objects. It’s a special case in the language to make type-checking functions easier.

Objects are the building blocks of almost everything you’ll write in JavaScript. We’ll go much deeper into objects in their own dedicated article later in the series. For now, just know that they are mutable (you can change their properties), and they behave very differently from primitives when you copy them. We’ll see exactly how in the demo below.


05The typeof Operator: Your Built-In Type Checker

When you need to know what type a value is at runtime, typeof is your go-to tool. You’ve already seen it used throughout the code examples above. Here’s a complete reference of what it returns for every type.

Reference Visual

The typeof Operator: Complete Output Reference

Every possible typeof result, including the two famous quirks (marked in red).

Valuetypeof resultNotes
“hello”“string”Any string value
42“number”Integers and floats
3.14“number”Floats are also “number”
NaN“number”⚠ NaN means “Not a Number” but its type is “number”
100n“bigint”BigInt values
true / false“boolean”Both values return “boolean”
undefined“undefined”Uninitialized variables
null“object”⚠ Historical bug! null is NOT an object, use === null to check
Symbol(“id”)“symbol”Any Symbol value
{}“object”Plain objects
[]“object”Arrays are objects! Use Array.isArray() to check
new Date()“object”Dates are objects
function() {}“function”Functions are objects but get special typeof treatment
Two quirks to memorize: 1) typeof null === "object" is a historical bug from 1995. Always use value === null to check for null specifically. 2) typeof NaN === "number" feels wrong, but NaN is technically a numeric value that represents an invalid calculation result. Use Number.isNaN(value) to check for NaN.

One more useful thing about typeof: it’s safe to use even on variables that haven’t been declared at all, unlike most other operations that would throw a ReferenceError.

// Accessing undeclared variable — throws error
// console.log(notDeclared);   // ReferenceError!

// But typeof on undeclared variable — safe!
console.log(typeof notDeclared);   // "undefined" (no error!)

// This is useful for feature checking
if (typeof window !== "undefined") {
  console.log("Running in the browser");
} else {
  console.log("Running in Node.js");
}

This pattern is common in code that needs to run in both browser and Node.js environments. You’ll see it a lot in real projects.


06How Primitives and Objects Are Stored Differently

This section is where a lot of beginners hit surprising bugs, so pay close attention. The difference is simple to explain but has big consequences in real code.

Primitives are copied by value. When you copy a primitive, you get a completely independent copy. Changing one does not affect the other.

Objects are copied by reference. When you copy an object (or array), you’re not copying the data. You’re copying the address that points to the data. Both variables end up pointing at the same thing in memory.

Use the demo below to see exactly what this looks like:

Interactive Demo

Copy by Value vs Copy by Reference

See what happens when you “copy” a primitive vs an object. Click the button to run the copy step.

Primitive (Number)

Step 1: Initial value

let a = 10

Step 2: Copy to b

let b = a  →  10

Step 3: Change b to 20

b = 20
a is still 10

Independent copy. b changed to 20 but a remains 10. Primitives don’t share.

Object

Step 1: Initial object

let userA = { name: “Daniyal” }

Step 2: Copy to userB

let userB = userA  →  same reference

Step 3: Change userB.name

userB.name = “Ali”
userA.name is now “Ali”

Shared reference! Changing userB.name also changed userA.name. They point to the same object in memory.


That shared reference behavior is one of the most common sources of hard-to-find bugs in JavaScript projects. You think you’re working with a copy, but you’re actually modifying the original. We’ll cover how to properly clone objects and arrays (using spread syntax and structured clone) in the upcoming articles on Objects and the Spread operator.


07Type-Based Bugs That Actually Show Up in Real Projects

Let me show you three specific type bugs that developers encounter regularly. Each one has a clear cause and a clear fix.

Bug 1: Adding Numbers That Come From Inputs

// Form inputs always give strings
let qty = "3";       // from an input field
let price = "9.99";  // also from an input

// Looks like math, but it's string concatenation
console.log(qty + price);   // "39.99" — wrong!

// Fix: convert to numbers first
let total = Number(qty) * Number(price);
console.log(total);   // 29.97 — correct

// Or use parseInt / parseFloat
let quantity = parseInt(qty, 10);   // 3
let unitPrice = parseFloat(price);  // 9.99
console.log(quantity * unitPrice);  // 29.97

Bug 2: Comparing a Value That Might Be Null

// Dangerous check — typeof null is "object" so this fails
function processUser(user) {
  if (typeof user === "object") {
    // This runs even when user is null!
    console.log(user.name);   // TypeError: Cannot read properties of null
  }
}

processUser(null);   // Crash!

// Safe check — always guard null explicitly
function processUserSafe(user) {
  if (user !== null && typeof user === "object") {
    console.log(user.name);   // Safe
  } else {
    console.log("No user provided");
  }
}

processUserSafe(null);           // "No user provided"
processUserSafe({ name: "Daniyal" });  // "Daniyal"

Bug 3: Checking If a Value Exists but Getting Confused by Falsy Primitives

// You're checking if a setting was provided
function applyDiscount(price, discountPercent) {
  // Bug: if discountPercent is 0, this treats it as "not provided"
  if (discountPercent) {
    return price - (price * discountPercent / 100);
  }
  return price;  // Skips 0% discount even when 0 was intentionally passed
}

console.log(applyDiscount(100, 10));   // 90 (correct)
console.log(applyDiscount(100, 0));    // 100 (wrong — 0% discount should still apply)

// Fix: check for undefined specifically
function applyDiscountFixed(price, discountPercent) {
  if (discountPercent !== undefined) {
    return price - (price * discountPercent / 100);
  }
  return price;
}

console.log(applyDiscountFixed(100, 10));    // 90 (correct)
console.log(applyDiscountFixed(100, 0));     // 100 (correct — 0% = no change)

That third one is subtle. The number 0 is falsy in JavaScript, so a plain if (discountPercent) check treats 0 as if nothing was passed. Always be specific about what you’re checking when dealing with numbers that could legitimately be zero.

A useful mental model: Ask yourself, “What type is this value, and is that what I actually need?” Before writing any comparison or calculation, make sure the types on both sides are what you expect. If they might not be, convert them explicitly. Type problems are almost always caused by assuming a type rather than verifying it.

08Quick Reference: All 8 JavaScript Data Types

Here’s a clean summary of everything we covered. Bookmark this or come back whenever you need a reminder.

// All 8 JavaScript data types in one place

// --- 7 Primitive Types ---
let str      = "hello";           // string
let num      = 42;                // number
let big      = 9007199254740993n; // bigint
let bool     = true;              // boolean
let undef;                        // undefined (no assignment)
let empty    = null;              // null (intentional empty)
let id       = Symbol("id");      // symbol

// --- 1 Object Type ---
let obj      = { name: "Daniyal" };   // object (plain)
let arr      = [1, 2, 3];             // object (array)
let fn       = function() {};          // function (object, special typeof)

// --- typeof for each ---
console.log(typeof str);     // "string"
console.log(typeof num);     // "number"
console.log(typeof big);     // "bigint"
console.log(typeof bool);    // "boolean"
console.log(typeof undef);   // "undefined"
console.log(typeof empty);   // "object"   ← quirk! use === null instead
console.log(typeof id);      // "symbol"
console.log(typeof obj);     // "object"
console.log(typeof arr);     // "object"   ← use Array.isArray() for arrays
console.log(typeof fn);      // "function" ← special case for callable objects

09What’s Coming Next in the Series

Now that you know all 8 types exist and what they represent, the next few articles dig deep into the ones you’ll use constantly. Starting with:

  • Strings in JavaScript: Single vs double vs template literals, all the essential string methods, and how string immutability actually works in practice.
  • Numbers in JavaScript: The floating point problem explained properly, NaN and Infinity, parseInt, parseFloat, and the Math object.
  • Booleans, null, and undefined: The six falsy values in JavaScript and how they feed into all your conditional logic.

Understanding JavaScript’s syntax rules helps a lot here because the type system ties directly into how expressions are evaluated and how the runtime makes decisions. If you skipped that article, it’s worth going back.

Types feel like a small thing until the moment they cause a bug at 11pm the night before a deadline. Learn them now, and you’ll spend a lot less time confused later. That’s the whole point.

JavaScript Data Types

Choose a difficulty to load your challenge

Loading challenge…

Ready to Practice?

Select a difficulty level above to load your challenge.

Easy

1
Live Preview
Great Work!