Understanding typeof and instanceof in TypeScript with Real-World Examples

Table of Content
In TypeScript, typeof and instanceof are operators for runtime type checks, each suited to different use cases. Let’s explore how they work, examine practical examples, and understand their limitations and warnings.
1. typeof Operator
The typeof operator checks the type of a variable at runtime, returning a string like "string", "number", "boolean", "object", or "undefined". It’s mainly used for checking the types of simple values.
Real-World Use Case for typeof
Imagine a function processing user input that could be a number (age), a string (name), or a boolean (active status). Using typeof, we handle each input type accordingly.
Example
function processUserInput(input: string | number | boolean) {
  if (typeof input === "string") {
    console.log(`Hello, ${input}!`); // Treats input as a name
  } else if (typeof input === "number") {
    console.log(`You are ${input} years old.`); // Treats input as an age
  } else if (typeof input === "boolean") {
    console.log(`User is ${input ? "active" : "inactive"}.`); // Treats input as a status
  } else {
    console.log("Unknown input type.");
  }
}
processUserInput("Alice"); // Output: "Hello, Alice!"
processUserInput(30); // Output: "You are 30 years old."
processUserInput(true); // Output: "User is active."In this example:
typeofallows us to distinguish the input’s type and handle it appropriately. This is helpful in form handling, configuration settings, or any code where user input might vary in type.
Limitations and Warnings with typeof
- Only Works with Primitive Types: 
typeofis limited to identifying simple types. It can’t distinguish between arrays and objects or identify custom types (e.g.,typeof []andtypeof {}both return"object"). - Misidentifies null as "object": Due to a legacy JavaScript quirk, 
typeof nullreturns"object", which can lead to confusion in type-checking conditions. - Cannot Identify Class Instances or Interfaces: Since 
typeofonly works with values, it doesn’t recognize class instances or interface types. Useinstanceofin those cases. 
2. instanceof Operator
The instanceof operator checks if an object is an instance of a specific class or constructor function. It’s useful when working with classes, especially in error handling and access control scenarios.
Real-World Use Case for instanceof
Suppose we have an API call wrapped in a try...catch block. We want to handle network errors differently from validation errors, so we use instanceof to distinguish the error type and respond appropriately.
Example
class NetworkError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "NetworkError";
  }
}
class ValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = "ValidationError";
  }
}
async function fetchData() {
  try {
    // Simulate an error for demonstration
    throw new NetworkError("Failed to connect to server.");
  } catch (error) {
    if (error instanceof NetworkError) {
      console.log("Please check your internet connection.");
    } else if (error instanceof ValidationError) {
      console.log("Data validation failed:", error.message);
    } else {
      console.log("An unknown error occurred:", error);
    }
  }
}
fetchData(); // Output: "Please check your internet connection."In this example:
NetworkErrorandValidationErrorare custom error classes.instanceofidentifies the error type, allowing us to respond with an appropriate message.
Using instanceof with Custom Classes
A common use case is working with role-based systems. Suppose we have User and Admin classes. We might use instanceof to check if a user is an Admin to grant specific privileges.
class User {
  constructor(public name: string) {}
}
class Admin extends User {
  constructor(
    name: string,
    public privileges: string[],
  ) {
    super(name);
  }
}
function checkUserRole(user: User) {
  if (user instanceof Admin) {
    console.log(`Admin privileges: ${user.privileges.join(", ")}`);
  } else {
    console.log(`User: ${user.name}`);
  }
}
const regularUser = new User("Alice");
const adminUser = new Admin("Bob", ["manage_users", "edit_content"]);
checkUserRole(regularUser); // Output: "User: Alice"
checkUserRole(adminUser); // Output: "Admin privileges: manage_users, edit_content"Limitations and Warnings with instanceof
- Only Works with Class Instances: 
instanceofis suitable only for objects created from a constructor or class. It won’t work with literals or primitive values (e.g.,number,string). - Limited by Execution Contexts: If objects are created in different JavaScript contexts (such as different iframes), 
instanceofmay not work as expected because each context has its own set of constructors. - Does Not Work with Interfaces: In TypeScript, interfaces are only used for compile-time checks and do not exist at runtime, so you cannot use 
instanceofto check if an object implements an interface. 
Key Differences Between typeof and instanceof
| Feature | typeof | instanceof | 
|---|---|---|
| Purpose | Checks primitive types | Checks if an object is an instance of a class or constructor | 
| Return Type | Returns a string | Returns a boolean | 
| Usage | Suitable for basic types | Suitable for custom classes and objects | 
| Type Guard | Works with primitive types | Works with classes and subclasses | 
| Limitations | Can’t identify custom classes or null | Doesn’t work with primitive types | 
Summary
- Use 
typeofto determine the type of primitive values such as strings, numbers, and booleans, especially in scenarios like handling user input or parsing data. - Use 
instanceofto verify an object’s class, particularly for custom error handling and permission systems in applications. 
Together, typeof and instanceof provide essential tools for runtime type-checking in TypeScript. By understanding their limitations and best use cases, you can write more dynamic, type-safe code.
