DEV Community

Yangholmes
Yangholmes

Posted on

JavaScript Variable Type Checking

This article was translated, original article here.

JavaScript Data Types

JavaScript is a loosely typed language, making type checking essential during variable usage. However, JavaScript doesn't strictly define primitive types. From a top-down perspective, there are only two categories: Primitive (primitive values, or primitive data types) and object. The distinction is simple: primitives lack properties and methods, while objects possess them. Primitives are immutable and represent the lowest-level data representation in JavaScript. Developers rarely interact directly with primitive values—when accessing them, JavaScript automatically wraps primitives in objects. For example, the string 'foo' is a primitive. When executing 'foo'.includes('f'), a String object is created to wrap 'foo'. The primitive itself has no methods/properties; it's actually accessing String.prototype.includes().

There are 7 primitive types:

  • string
  • number
  • bigint
  • boolean
  • undefined
  • symbol
  • null

Except for undefined and null, each primitive is wrapped into a corresponding object to enable property/method access. Attempting to access properties/methods on null or undefined throws errors, proving primitives inherently lack these features.

Type Object Wrapper
Null N/A
Undefined N/A
Boolean Boolean
Number Number
BigInt BigInt
String String
Symbol Symbol

Beyond primitives, JavaScript offers built-in objects like Array and Date to represent collections. All such objects inherit from Object.prototype, including Function.

Thus, JavaScript type checking fundamentally involves inspecting variable primitives and prototypes.

Type Checking Solutions

typeof

typeof returns the type of the operand's value.

Usage

typeof operand
Enter fullscreen mode Exit fullscreen mode
console.log(typeof true);
// Outputs "boolean"
Enter fullscreen mode Exit fullscreen mode
Type Result
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
BigInt "bigint"
String "string"
Symbol "symbol"
Function "function"
Any other object "object"

This is the most direct way to check primitives. Historically, typeof null returns "object" due to a bug: early JavaScript values used a type tag + value structure. Both objects (000 tag) and null (memory address 0x00) shared the 000 tag. The typeof implementation only checks executability (functions are executable, other 000 are objects), causing null misidentification.

For historical context on typeof null, see this article.

Strengths: Simple.
Weaknesses: Cannot check non-function built-in objects or custom objects; requires extra code for null.

instanceof

instanceof checks if a constructor's prototype exists in an object's prototype chain.

Usage

object instanceof constructor
Enter fullscreen mode Exit fullscreen mode
function Man(age) {
  this.age = age;
}
const k = new Man(30);

console.log(k instanceof Man); // true

console.log(k instanceof Object); // true (inherits from Object)
Enter fullscreen mode Exit fullscreen mode

When executing object instanceof constructor, JavaScript checks for Symbol.hasInstance on the constructor. If absent, it traverses the prototype chain until Object.prototype.

Symbol.hasInstance can be overridden, leading to inaccurate results:

class FakeArray {
  static [Symbol.hasInstance](instance) {
    return Array.isArray(instance);
  }
}

console.log([] instanceof FakeArray);  // true (custom behavior)
Enter fullscreen mode Exit fullscreen mode

Strengths: Effective for instance type checks.
Weaknesses: Fails for null/undefined; results can be manipulated via Symbol.hasInstance.

constructor

An instance's constructor property references its creating constructor.

Usage

obj.constructor
Enter fullscreen mode Exit fullscreen mode

All objects have a constructor property (unless explicitly set to null). This avoids prototype chain traversal.

Object.prototype.toString.call()

toString() returns an object's string representation. Native Object.prototype.toString returns '[object Type]'.

Exploiting this:

console.log(Object.prototype.toString.call([])); // "[object Array]"
Enter fullscreen mode Exit fullscreen mode

Results can be altered via Symbol.toStringTag:

class FakeArray {
  get [Symbol.toStringTag]() {
    return 'Array';
  }
}
console.log(Object.prototype.toString.call(new FakeArray())); // "[object Array]"
Enter fullscreen mode Exit fullscreen mode

Summary

Method Strengths Weaknesses Use Cases
typeof Simple and intuitive; most convenient for primitive checks Cannot check null or various objects API data (often primitives)
instanceof Traverses entire prototype chain thoroughly Cannot check null, undefined or primitives; results can be manipulated General object type checks
constructor Constructor remains unmodified; accurate Fails for null-constructor objects; requires constructor comparison; cumbersome for IIFE/anonymous functions General object type checks
toString Returns descriptive results; easy to use Verbose code; results can be modified General object type checks

No single solution is a "silver bullet." Combining methods is often necessary. Mozilla provides a comprehensive implementation example for reference.

Top comments (0)

Some comments may only be visible to logged-in visitors. Sign in to view all comments.