Primitive and Non-primitive Data Types in JavaScript: Explained in Plain English

Primitive and Non-primitive Data Types in JavaScript: Explained in Plain English

If you’re just getting started with a new programming language, and more so if you’re still learning the ropes of programming, you might feel overwhelmed by all the jargon thrown at you. Primitive data types, arrays, data structures, flow control… it’s just pretty easy to get lost in the sea of terminology. That shouldn’t deter or get in your way, however.  

In this article, we’ll start with the basics and focus on one of the most important concepts in programming: data types. We’ll explain what they are and why they matter, and then dive into the different primitive and non-primitive data types available in JavaScript.

Data Types in Programming

In programming, a data type is a classification of data that determines the type of values a variable can hold. Different programming languages have different data types, and it’s essential to understand them to write effective programs.

JavaScript is a dynamically typed language, which means that you don’t have to declare the data type of a variable before using it. Instead, JavaScript will automatically determine the data type based on the value assigned to the variable. This feature makes JavaScript more flexible than statically typed languages, but it also means that you need to be aware of the different data types and their behaviors.

We can divide data types into two categories: primitive and non-primitive data types. Primitive data types are the building blocks of data manipulation in JavaScript as well as other programming languages. On the other hand, non-primitive data types are derived from primitive data types and can store complex data types.

Primitive Data Types in JavaScript

Primitive data types, as the name suggests, are the most basic data types in JavaScript and are built into the language. They are immutable, which means that they cannot be altered once they are created.

In JavaScript, there are seven primitive data types namely Number, String, Boolean, Null, Undefined, Symbol, and BigInt. We’ll look at each and every one of them in more depth.


The Number primitive data type is used for numeric values like integers, decimals, and exponential values. It represents both integer and floating-point numbers and is commonly used in a wide range of programming applications. The number data type also includes NaN (Not a Number), which is a numeric value that represents an undefined or unrepresentable value.

The range of the number data type in JavaScript is quite extensive, from -253 – 1 to 253 – 1 for integers, and up to 1.79 × 10308 for floating-point numbers.

While working with floating-point numbers, it’s good to keep in mind that their precision could actually be a source of errors in JavaScript. For example, adding 0.1 and 0.2 might not result in the expected 0.3 because of the way that floating-point numbers are represented in JavaScript. Consider the following example:

let num1 = 0.1;
let num2 = 0.2;

console.log(num1 + num2); // Outputs 0.30000000000000004 instead of 0.3

To avoid such errors, you can use methods such as toFixed() to control the number of decimal places:

let result = (num1 + num2).toFixed(1); // Rounds off to 1 decimal place

console.log(result); // Outputs 0.3

Type-casting Numbers

Type-casting or coercion can also be done with the number data type. Type coercion is the process of converting a value from one data type to another. JavaScript is a loosely typed language, which means that type coercion can happen implicitly or explicitly.

Implicit coercion happens when JavaScript automatically converts one data type to another. For example, if you add a number and a string, JavaScript will convert the string to a number and then add the two values.

Explicit coercion happens when you manually convert one data type to another. For example, JavaScript can convert string literals to numeric values using the parseInt() or parseFloat() functions.

Here’s an example of type casting using the parseInt() function:

let myString = "10";
let myNumber = parseInt(myString);

console.log(typeof myString); // Outputs "string"
console.log(typeof myNumber); // Outputs "number"

However, keep in mind that if the string cannot be converted to a number, the function will return NaN. In addition, adding a string and a number will result in a concatenated string rather than a mathematical operation.

A common pitfall to avoid with the number data type is treating NaN as a regular number. NaN is not equal to any value, including itself, so using the === operator to compare NaN will always result in false. Instead, use the isNaN() function to determine whether a value is NaN.


Another important primitive data type in JavaScript is the String data type. In simple terms, a string is a collection of characters, which can be letters, numbers, symbols, or spaces. It is one of the most commonly used data types in programming, especially when it comes to handling text-based data.

In JavaScript, a string is created by enclosing a sequence of characters within single or double quotation marks. 

const myString = "Hello, world!";

Being a primitive data type, strings are immutable. However, it is possible to create a new string by concatenating two or more strings using the “+” operator.

const string1 = "Hello";
const string2 = "world";
const concatenatedString = string1 + " " + string2;

It can be easy to confuse them with other data types, especially when it comes to type coercion. For example, the addition operator (“+”) can be used to concatenate strings, but if one of the operands is a number, it will be automatically converted to a string. This can sometimes lead to unexpected results, so it’s important to keep this in mind when working with strings in JavaScript.

Another useful feature of strings in JavaScript is the ability to access individual characters using bracket notation. For example, to access the first character of a string, you can use string[0].

let myString = "Hello, world!";
console.log(myString[0]); // Output: "H"
console.log(myString[7]); // Output: "w"


As with almost all other programming languages, the Boolean data type in JavaScript is used to represent logical values, true or false. It is a simple and important data type in programming. Boolean values are used for making decisions in code, such as if/else statements and loops. The true and false values can also be used as flags to indicate the status of a program or a feature.

One common use of the boolean data type is in form validation. For example, you can use boolean values to check whether a user has filled out all required fields in a form before submitting it.

const nameInput = document.getElementById("name");
const emailInput = document.getElementById("email");
const isFormValid = nameInput.value !== "" && emailInput.value !== "";
if (isFormValid) {
  // Submit form
} else {
  // Show error message

Boolean values can also be generated through comparison operators, such as greater than or less than.

const age = 18;
const isAdult = age >= 18;

Do keep in mind that while JavaScript has a built-in boolean data type, not all values are automatically evaluated as true or false. For example, an empty string, zero, and null will be evaluated as false, but an empty array or object will be evaluated as true.

Type casting can also lead to unexpected results when working with boolean values. For example, the number 0 will be coerced to false, but the string “0” will be coerced to true below.

// Example of type coercion with Boolean values
const num = 0;
const str = "0";
console.log(Boolean(num)); // false
console.log(Boolean(str)); // true

That’s why it’s a good idea to keep in mind the rules for type coercion and to explicitly convert values to the appropriate type when needed.


The primitive data type Null in JavaScript represents the intentional absence of any object value. It is often used to indicate that a variable has no value or that a function returns nothing. 

Null has no sub-types or ranges and can only have one value, which is the keyword ‘null’. This value is often used as a placeholder to represent missing or unknown data. 

Here’s an example of how null can be used in JavaScript:

let myVar = null;
console.log(myVar); // Output: null

It’s important to keep in mind that null is a special value and cannot be treated like a number or a string. Attempting to perform mathematical operations or concatenate strings with null can result in errors. 

One often-made error is using the ‘==’ operator to compare a variable with null, which can lead to unexpected behavior. To avoid this, it is recommended to always use the ‘===’ operator, which checks for both value and type equality. 


Undefined is a primitive data type in JavaScript that is used to represent the absence of a value. When a variable is declared but not assigned a value, its default value is undefined. Similarly, if a function doesn’t return a value, its default return value is also undefined.

Undefined indicates a missing property in an object. When you try to access a property that doesn’t exist in an object, the value returned is undefined.

One common mistake with undefined is attempting to access a property of an undefined variable. This will result in a TypeError, so it’s important to check if a variable is undefined before trying to access its properties.

let myVariable;
console.log(myVariable); // logs undefined
let myObject = {};
console.log(myObject.missingProperty); // logs undefined
if (typeof myVariable !== 'undefined') {
  console.log(myVariable.missingProperty); // this line will not execute

Some programmers fall into the trap of using it in arithmetic operations. Since undefined represents the absence of a value, any arithmetic operation involving undefined will result in NaN (Not a Number).

let x;
let y = 5;
console.log(x + y); // logs NaN

As well, being a primitive data type and not an object, it doesn’t have any properties or methods of its own. Trying to access them will result in a TypeError.

console.log(undefined.toString()); // TypeError: Cannot read property 'toString' of undefined

Undefined vs Null

As we’ve seen both undefined and null represent the absence of a value. They’re also both falsy values in JavaScript, which means they evaluate to false in conditional statements. However, they have different use cases and behaviors. Undefined is used to indicate the absence of a value that has not been initialized or assigned. Conversely, null is used to represent the intentional absence of any object value. 

In other words undefined indicates a variable or property does not exist or has not been given a value while null is used to explicitly indicate the absence of a value or as a placeholder to represent missing or unknown data.

One important difference between the two is that attempting to access a property or method of an undefined variable will result in a TypeError, while null can be used as an object value and has properties and methods of its own.


Symbols are a unique data type in JavaScript that was introduced in ECMAScript 6. They are used to represent a unique identifier that is guaranteed to be unique and immutable. A symbol is created using the ‘Symbol()’ function, which returns a new symbol each time it is called. Unlike other primitive data types in JavaScript, symbols have no literal representation. They can only be created using the ‘Symbol()’ function.

One of the main use cases for symbols is to create unique property keys in objects. This helps avoid naming collisions when multiple objects have properties with the same name. Here’s an example:

const mySymbol = Symbol();
const myObject = {};

myObject[mySymbol] = 'Hello, world!';
console.log(myObject[mySymbol]); // Output: 'Hello, world!'

Symbols also have a few built-in properties and methods, such as ‘Symbol.iterator’, which is used to create an iterator for a collection.

Although the symbols themselves are unique, their descriptions can be the same, which can lead to confusion and unexpected behavior. It’s also important to remember that symbols are not enumerable, which means they won’t show up in ‘for…in’ loops.


If you’ve ever worked with large numbers in JavaScript, you may have run into the issue where the number you’re working with exceeds the maximum safe integer value. This is where the BigInt data type comes in handy. 

BigInt is a primitive data type introduced in ECMAScript 2020 and is used to represent integers with arbitrary precision. Unlike the number data type, BigInt can handle numbers larger than 253 – 1, which is the maximum value of a safe integer as we saw. 

To create a BigInt, append the letter ‘n’ to the end of the number literal. For example: 

const bigNumber = 1234567890n;

One thing to keep in mind is that BigInts cannot be used with operators that only work with regular numbers, such as the modulus operator (%), and they cannot be mixed with regular numbers in mathematical operations. 

Another thing to keep in mind is that BigInts cannot be implicitly converted to regular numbers, and vice versa. You must explicitly convert between the two using the Number() and BigInt() functions. 

Here’s an example of using BigInt in JavaScript: 

const a = 900719925124740999n; // maximum safe integer
const b = 100n;
const c = a + b;
console.log(c); // output: 900719925124740999n

Non-Primitive Data Types in JavaScript

Non-primitive data types are derived from primitive data types. They can store complex data types, including functions. The most common non-primitive data types in JavaScript are object and array.


An object is a collection of properties and values that are grouped together to represent a single entity. The properties of an object are identified by their keys, and their values can be of any data type. Objects are mutable, meaning that their values can be changed once they are created.

In JavaScript, objects can be created using object literals or by using the object constructor function. We could create an object using the former like so:

const person = {
  firstName: "John",
  lastName: "Doe",
  age: 30,
  address: {
    street: "123 Main St",
    city: "Anytown",
    state: "CA"
  hobbies: ["reading", "hiking", "swimming"],
  isEmployed: true

This creates an object called person with various properties such as firstName, lastName, age, address, hobbies, and isEmployed. The address property is itself an object with its own properties.

Alternatively, an object can be created using the Object constructor function:

const person = new Object();
person.firstName = "John";
person.lastName = "Doe";
person.age = 30;

This creates an object called person and adds properties to it using dot notation.

Working with Objects

One of the most powerful features of objects is their ability to hold functions as properties as illustrated below:

const myObj = {
  name: 'John',
  age: 30,
  greet: function() {
    console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`);

myObj.greet(); // Output: Hello, my name is John and I am 30 years old.

This feature makes objects capable of executing complex logic and computations. It also allows for the creation of custom methods on objects, which can be especially useful in object-oriented programming.

Nonetheless, keep in mind that objects are reference types, which means that when they are assigned to a variable or passed as an argument, it’s the reference to the object that gets copied, not the object itself. This could lead to unexpected behavior if not handled carefully.

To avoid common mistakes with objects, it’s recommended to use the typeof operator as we did earlier to check if a value is an object before attempting to access its properties.

Using the object created earlier;

if (typeof myObj === 'object' && myObj.hasOwnProperty('name')) {
  console.log(myObj.name); // Outputs 'John'

It’s good practice to use the hasOwnProperty method to check if an object has a specific property before accessing it.

if (myObj.hasOwnProperty('address') && myObj.address.hasOwnProperty('city')) {
  console.log(myObj.address.city); // Outputs 'Anytown'

Of course, you only need to check for properties that you know might not exist in the object so you won’t have to unnecessarily clutter your code with checks for all properties.


Arrays are one of the most commonly used data types in JavaScript. They are a type of object that stores multiple values in a single variable, which can be of any data type. Arrays are denoted by square brackets, and each value within the array is separated by a comma.

Arrays can be used in a variety of ways in JavaScript. They can be used to store a list of values, such as a list of names or numbers. They can also be used to store complex data structures, such as a list of objects.

Working with Arrays

Arrays in JavaScript can be created using array literals or the Array constructor function.

// Using array literals
let arr1 = [1, 2, 3, 4, 5];

// Using the Array constructor
let arr2 = new Array(1, 2, 3, 4, 5);

Array elements are accessed using their index. The first element has an index of 0, the second element has an index of 1, and so on.

console.log(arr[0]); // Outputs 1
console.log(arr[2]); // Outputs 3

Elements can be added or removed from an array using various built-in methods.

arr.push(6); // Adds 6 to the end of the array
arr.unshift(0); // Adds 0 to the beginning of the array

arr.pop(); // Removes the last element (6) from the array
arr.shift(); // Removes the first element (0) from the array

Array Methods

Arrays in JavaScript come with many built-in methods that allow for powerful and flexible manipulation of their elements. Using our previous array example, let’s have a look at a few array methods;

// Get array length
console.log(arr.length); // Outputs 5

// Array iteration
arr.forEach(function(element) {

// Filtering
let filteredArr = arr.filter(function(element) {
  return element > 3;

console.log(filteredArr); // Outputs [4, 5]

// Array mapping
let mappedArr = arr.map(function(element) {
  return element * 2;

console.log(mappedArr); // Outputs [2, 4, 6, 8, 10]

One common mistake with arrays is assuming that they are always homogeneous. While JavaScript arrays can technically hold any type of value, it’s best practice to keep them homogeneous for easier manipulation. Furthermore, when iterating over arrays using a for…in loop, make sure to check that the current property is not inherited from the array’s prototype to avoid unintended behavior.

Some Best Practices While Working With JavaScript Data Types

We’ve already mentioned this before but it’s worth reiterating that it’s crucial to use strict equality checks (===) instead of loose equality checks (==) to avoid unexpected type coercion. Loose equality checks can lead to unexpected results and head-scratching errors.

To prevent potential errors, it’s recommended to use built-in functions for type conversion instead of relying on implicit type coercion. JavaScript has several built-in functions like parseInt() and parseFloat() that help convert data types as we said, so it’s best to utilize them rather than leaving it to chance with implicit type coercion.

Lastly, it’s always a good idea to initialize variables before use to avoid undefined values. This can be done by setting default values or using null values to indicate the absence of a value.

By following these best practices, you can avoid those pesky hard-to-catch bugs and write more reliable code.

Data Types in JavaScript: Rounding Up

Getting a good grasp of JavaScript’s data types is crucial for writing effective code and building robust applications. As we’ve seen, JavaScript has seven primitive data types namely number, string, boolean, null, undefined, symbol, and BigInt, and two non-primitive data types, objects, and arrays. 

Primitive data types are simple and do not have methods, while non-primitive data types are dynamic and do not have a fixed size. It is important to know the difference between primitive and non-primitive data types because they are treated differently by the computer. 

Remember to follow the best practices we’ve discussed such as using strict equality checks and utilizing built-in functions for type conversion. By mastering JavaScript data types JavaScript, you should be well on your way to shipping high-quality code and exploring more advanced features of the language to build more complex applications.

Frequently Asked Questions

What’s the main difference between primitive and non-primitive data types?

The primary difference between primitive and non-primitive data types is that primitive data types are immutable, while non-primitive data types are mutable. Another difference is that primitive data types are passed by value, while non-primitive data types are passed by reference.

How do JavaScript and TypeScript compare in terms of data types?

JavaScript is a dynamically typed language, meaning that the data type of a variable is determined at runtime. On the other hand, TypeScript is a statically typed language, meaning that the data type of a variable is determined at compile-time. TypeScript provides more strict typing, which could help prevent type-related errors and improve overall code quality.

What is the difference between == and === in JavaScript?

The == operator performs type coercion, meaning that it converts the operands to a common data type before performing the comparison. The === operator, on the other hand, performs a strict comparison, meaning that it does not perform type coercion and requires both operands to have the same data type and value.

What is the null data type in JavaScript?

The null data type represents a deliberate non-value. It is typically used to indicate that a variable should have no value or that a value does not exist. It is different from undefined, which indicates that a variable has not been initialized.

To top