Table of Contents
- Introduction
- What Are Type Guards in TypeScript?
typeof
Type Guard- Definition and Syntax
- Use Cases and Examples
instanceof
Type Guard- Definition and Syntax
- Use Cases and Examples
- Practical Examples
- Example 1: Using
typeof
to Narrow Types - Example 2: Using
instanceof
to Narrow Types
- Example 1: Using
- When and Why to Use Type Guards
- Conclusion
Introduction
TypeScript is a statically-typed superset of JavaScript, and it provides powerful type-checking features that improve the reliability of your code. One of the most helpful features of TypeScript is type guards, which allow you to narrow down the type of a variable within a certain block of code. This is particularly useful when you’re working with unions or any type, and you want to check the type of a value before performing specific operations.
In this article, we’ll focus on two built-in type guards in TypeScript: typeof
and instanceof
. These are simple yet powerful tools that enable you to check and narrow the types of variables effectively.
What Are Type Guards in TypeScript?
Type guards are expressions that allow TypeScript to narrow down the type of a variable within a certain scope. TypeScript uses these guards to refine the type of a variable when the code runs, allowing for more precise type checking and safer operations.
Type guards can be built-in (like typeof
and instanceof
), user-defined (such as custom functions that return a boolean), or even inferred by TypeScript itself based on context.
In this article, we will specifically focus on the two most commonly used built-in type guards: typeof
and instanceof
.
typeof
Type Guard
Definition and Syntax
The typeof
operator is used to determine the type of a primitive value (e.g., number, string, boolean, etc.). In TypeScript, typeof
can also be used as a type guard to narrow down the type of a variable based on its value.
The syntax for typeof
is:
if (typeof variable === "type") {
// TypeScript knows the type of 'variable' inside this block
}
Here, the typeof
operator checks the runtime type of the variable, and if it matches the specified type (e.g., "string"
, "number"
, "boolean"
, etc.), TypeScript narrows the type of that variable.
Use Cases and Examples
- Primitives:
typeof
is particularly useful when you are working with primitive types, such asstring
,number
,boolean
,symbol
,undefined
, andobject
. It can help refine the type when you deal with union types or variables of typeany
.
Example:
function printValue(value: number | string) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // Here, TypeScript knows 'value' is a string
} else if (typeof value === "number") {
console.log(value.toFixed(2)); // Here, 'value' is narrowed to a number
}
}
printValue("hello"); // Output: HELLO
printValue(123.456); // Output: 123.46
In this example, typeof
is used to check if value
is a string
or number
, and based on that, TypeScript knows the exact type of value
inside each conditional block, allowing you to safely call methods specific to each type (e.g., toUpperCase
for strings and toFixed
for numbers).
instanceof
Type Guard
Definition and Syntax
The instanceof
operator is used to check if an object is an instance of a specific class or constructor function. It’s particularly useful when you want to narrow the type of a class-based object.
The syntax for instanceof
is:
if (variable instanceof Class) {
// TypeScript narrows the type of 'variable' to 'Class' inside this block
}
Here, instanceof
checks whether variable
is an instance of the Class
. If it is, TypeScript narrows the type of variable
to that class type, allowing access to class-specific properties and methods.
Use Cases and Examples
- Class Instances:
instanceof
is used to check whether an object is an instance of a particular class, allowing you to work with class-specific properties and methods without TypeScript complaining about incorrect types. - Inheritance:
instanceof
can also be used to check if an object is an instance of a subclass, making it easy to handle objects that inherit from a common base class.
Example:
class Car {
constructor(public model: string, public year: number) {}
}
class Bike {
constructor(public brand: string, public type: string) {}
}
function printVehicleInfo(vehicle: Car | Bike) {
if (vehicle instanceof Car) {
console.log(`Car Model: ${vehicle.model}, Year: ${vehicle.year}`);
} else if (vehicle instanceof Bike) {
console.log(`Bike Brand: ${vehicle.brand}, Type: ${vehicle.type}`);
}
}
const myCar = new Car('Toyota', 2021);
const myBike = new Bike('Yamaha', 'Sport');
printVehicleInfo(myCar); // Output: Car Model: Toyota, Year: 2021
printVehicleInfo(myBike); // Output: Bike Brand: Yamaha, Type: Sport
In this example, instanceof
is used to check whether the vehicle
is an instance of Car
or Bike
, and TypeScript narrows the type accordingly inside each if
block. This ensures type-safe access to properties specific to each class.
Practical Examples
Example 1: Using typeof
to Narrow Types
function processValue(value: string | number) {
if (typeof value === "string") {
console.log(value.length); // 'value' is narrowed to a string here
} else {
console.log(value.toFixed(2)); // 'value' is narrowed to a number here
}
}
processValue("hello"); // Output: 5
processValue(123.456); // Output: 123.46
In this example, typeof
allows TypeScript to refine the type of value
based on whether it’s a string or a number, enabling safe method calls like length
for strings and toFixed
for numbers.
Example 2: Using instanceof
to Narrow Types
class Shape {
constructor(public name: string) {}
}
class Circle extends Shape {
constructor(name: string, public radius: number) {
super(name);
}
}
class Rectangle extends Shape {
constructor(name: string, public width: number, public height: number) {
super(name);
}
}
function calculateArea(shape: Shape) {
if (shape instanceof Circle) {
return Math.PI * shape.radius ** 2; // Narrowed to Circle type
} else if (shape instanceof Rectangle) {
return shape.width * shape.height; // Narrowed to Rectangle type
} else {
throw new Error("Unknown shape");
}
}
const circle = new Circle("Circle", 10);
const rectangle = new Rectangle("Rectangle", 20, 30);
console.log(calculateArea(circle)); // Output: 314.1592653589793
console.log(calculateArea(rectangle)); // Output: 600
In this example, instanceof
is used to narrow the type of shape
to Circle
or Rectangle
, allowing the correct formula to be applied based on the shape type.
When and Why to Use Type Guards
typeof
: Usetypeof
when dealing with primitive types such as strings, numbers, and booleans, or when working with union types. It is a simple and effective way to check the type at runtime.instanceof
: Useinstanceof
when working with class-based types or when dealing with inheritance. It allows TypeScript to infer and narrow the type of objects that are instances of specific classes, ensuring type safety for class methods and properties.
Both typeof
and instanceof
are integral tools for narrowing down types in TypeScript, especially when working with complex or dynamic data structures.
Conclusion
In this article, we’ve explored two powerful built-in type guards in TypeScript: typeof
and instanceof
. These operators allow you to narrow the types of variables within specific blocks of code, enabling type-safe operations and reducing runtime errors.
By using typeof
for primitive types and instanceof
for class instances, you can handle dynamic data structures more effectively and ensure that your code adheres to TypeScript’s type safety features.