TYPESCRIPT
TypeScript Type Guard: Syntax, Usage, and Examples
A TypeScript type guard is a function or construct that lets you check and narrow down types at runtime. It helps you ensure that variables or values conform to expected types before performing operations on them. Type guards improve type safety, prevent runtime errors, and make code more predictable.
How to Use Type Guards in TypeScript
A type guard is a function that returns a boolean value based on whether a given value matches a specific type. The most common syntax follows this pattern:
function isString(value: unknown): value is string {
return typeof value === "string";
}
You can use this function inside conditional statements to narrow the type of a variable:
function printLength(value: unknown) {
if (isString(value)) {
console.log(value.length); // TypeScript knows 'value' is a string here
} else {
console.log("Value is not a string");
}
}
printLength("Hello"); // Output: 5
printLength(42); // Output: Value is not a string
Using typeof
for Primitive Type Guards
The typeof
operator is useful for checking primitive types:
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
console.log(isNumber(10)); // true
console.log(isNumber("hello")); // false
Using instanceof
for Object Type Guards
To check whether a value is an instance of a class or a specific object type, use instanceof
:
class Dog {
bark() {
console.log("Woof!");
}
}
function isDog(obj: unknown): obj is Dog {
return obj instanceof Dog;
}
const pet = new Dog();
console.log(isDog(pet)); // true
When to Use Type Guards in TypeScript
Type guards are useful when working with:
- Union types: If a variable can hold multiple types, type guards ensure that you correctly handle each case.
- Custom type checks: When TypeScript’s built-in type inference is insufficient, type guards help define custom logic to differentiate types.
- Filtering arrays: You can filter arrays based on type-specific conditions.
Type guards help prevent runtime errors caused by incorrect type assumptions.
Examples of Type Guards in TypeScript
Checking for Specific Types in a Union
You often need to determine the actual type of a variable before working with it. Type guards make this easier:
function processInput(input: string | number) {
if (typeof input === "string") {
console.log(`String length: ${input.length}`);
} else {
console.log(`Number squared: ${input * input}`);
}
}
processInput("Hello"); // Output: String length: 5
processInput(5); // Output: Number squared: 25
Custom Type Guards for Objects
When working with objects that have different structures, you can define custom type guards:
interface Car {
make: string;
model: string;
}
interface Bicycle {
brand: string;
gearCount: number;
}
function isCar(vehicle: Car | Bicycle): vehicle is Car {
return (vehicle as Car).make !== undefined;
}
const myCar: Car = { make: "Toyota", model: "Corolla" };
const myBike: Bicycle = { brand: "Giant", gearCount: 21 };
console.log(isCar(myCar)); // true
console.log(isCar(myBike)); // false
Filtering an Array with a Type Guard
A type guard can filter arrays to ensure only values of a certain type remain:
function isStringArray(value: unknown[]): value is string[] {
return value.every((item) => typeof item === "string");
}
const mixedArray: (string | number)[] = ["apple", "banana", 42, "grape"];
const stringArray = mixedArray.filter((item): item is string => typeof item === "string");
console.log(stringArray); // Output: ["apple", "banana", "grape"]
Using Type Guards for Enums
Type guards also work with enums to restrict values to a specific subset:
enum UserRole {
Admin = "admin",
User = "user",
}
function isAdmin(role: UserRole): role is UserRole.Admin {
return role === UserRole.Admin;
}
const userRole: UserRole = UserRole.User;
if (isAdmin(userRole)) {
console.log("User has admin privileges");
} else {
console.log("User is not an admin");
}
Learn More About Type Guards in TypeScript
Generic Type Guards
If you need a type guard that works for multiple types, use a generic function:
function isArrayOfType<T>(arr: unknown[], checkFn: (item: unknown) => item is T): arr is T[] {
return arr.every(checkFn);
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
const values: unknown[] = [1, 2, "hello", 4];
if (isArrayOfType<number>(values, isNumber)) {
console.log("All elements are numbers");
} else {
console.log("Not all elements are numbers");
}
Type Guards for Interfaces
When dealing with interfaces, type guards help verify object properties:
interface Person {
name: string;
age: number;
}
function isPerson(obj: any): obj is Person {
return typeof obj.name === "string" && typeof obj.age === "number";
}
const user = { name: "Alice", age: 25 };
console.log(isPerson(user)); // true
Using Type Guards with Type Assertions
Sometimes, you may need to use a type assertion to guide TypeScript:
function isDefined<T>(value: T | undefined | null): value is T {
return value !== undefined && value !== null;
}
const data: (string | null)[] = ["Hello", null, "World"];
const filteredData = data.filter(isDefined);
console.log(filteredData); // Output: ["Hello", "World"]
Type Guards vs. Type Assertions
While type guards provide runtime checks, type assertions (as Type
) override TypeScript’s type system without checking values. Type guards are safer because they prevent incorrect assumptions:
const unknownValue: unknown = "Hello";
// Unsafe: Type assertion without validation
const unsafeString: string = unknownValue as string;
// Safe: Using a type guard
if (isString(unknownValue)) {
const safeString: string = unknownValue;
console.log(safeString); // Output: Hello
}
Type Guards for String Literals
If you need to check whether a value matches a specific string literal, use a type guard:
type Color = "red" | "green" | "blue";
function isColor(value: unknown): value is Color {
return value === "red" || value === "green" || value === "blue";
}
const userColor: string = "red";
if (isColor(userColor)) {
console.log(`Valid color: ${userColor}`);
} else {
console.log("Invalid color");
}
TypeScript type guards let you refine types at runtime, making your code safer and reducing type errors. They are essential when working with union types, dynamic objects, or complex data structures.
Sign up or download Mimo from the App Store or Google Play to enhance your programming skills and prepare for a career in tech.