bugl
bugl
HomeLearnPatternsPathsSearchPremium
HomeLearnPatternsPaths
Learn/TypeScript/TypeScript Core
TypeScript•TypeScript Core

TypeScript Type Guards

Understanding Type Guards in TypeScript

TypeScript Type Guards are powerful constructs that allow you to narrow down the type of a variable within a specific scope.

They help TypeScript understand and enforce type safety by providing explicit checks that determine the specific type of a variable at runtime.

Why Use Type Guards?

  • Type Safety : Ensure operations are only performed on appropriate types
  • Code Clarity : Make type checking explicit and self-documenting
  • Better Tooling : Get accurate IntelliSense and code completion
  • Error Prevention : Catch type-related errors at compile time
  • Runtime Safety : Add an extra layer of type checking at runtime

Type Guard Patterns

  • typeof type guards
  • instanceof type guards
  • User-defined type guards with type predicates
  • Discriminated unions with literal types
  • in operator type guards
  • Type assertion functions

typeof Type Guards

The typeof operator is a built-in type guard that checks the type of a primitive value at runtime.

It's particularly useful for narrowing primitive types like strings, numbers, booleans, etc.

Basic Usage

Use typeof checks to narrow primitive unions inside conditional branches.

Example

// Simple type guard with typeof function formatValue(value: string | number): string {
if (typeof value === 'string') {
  // TypeScript knows value is string here
  return value.trim().toUpperCase();
} else {
// TypeScript knows value is number here
return value.toFixed(2);
}
}
// Example usage
const result1 = formatValue('  hello  ');  // "HELLO"
const result2 = formatValue(42.1234);      // "42.12"

In the above example, TypeScript understands the type of value in different branches of the if statement.

instanceof Type Guards

The instanceof operator checks if an object is an instance of a specific class or constructor function.

It's useful for narrowing types with custom classes or built-in objects.

Class-based Type Guarding

Narrow unions of class instances by checking the constructor with instanceof .

Example

class Bird {
  fly() {
    console.log("Flying...");
  }
}
class Fish {
  swim() {
    console.log("Swimming...");
  }
}
function move(animal: Bird | Fish) {
  if (animal instanceof Bird) {
    // TypeScript knows animal is Bird here animal.fly();
  } else {
  // TypeScript knows animal is Fish here animal.swim();
}
}

User-Defined Type Guards

For more complex type checking, you can create custom type guard functions using type predicates.

These are functions that return a type predicate in the form parameterName is Type .

Type Predicate Functions

Return a predicate like value is Type so TypeScript narrows on the true branch.

Example

interface Car {
  make: string;
  model: string;
  year: number;
}
interface Motorcycle {
  make: string;
  model: string;
  year: number;
  type: "sport" | "cruiser";
}
// Type predicate function function isCar(vehicle: Car | Motorcycle): vehicle is Car {
return (vehicle as Motorcycle).type === undefined;
}
function displayVehicleInfo(vehicle: Car | Motorcycle) {
  console.log(`Make: ${vehicle.make}, Model: ${vehicle.model}, Year: ${vehicle.year}`);
  if (isCar(vehicle)) {
    // TypeScript knows vehicle is Car here console.log("This is a car");
  } else {
  // TypeScript knows vehicle is Motorcycle here console.log(`This is a ${vehicle.type} motorcycle`);
}
}

The function signature vehicle is Car is a type predicate that tells TypeScript to narrow the type when the function returns true.

Discriminated Unions

Discriminated unions (also known as tagged unions) use a common property (the discriminant) to distinguish between different object types in a union.

This pattern is particularly powerful when combined with type guards.

Basic Discriminated Union

Use a shared literal property (like kind ) to switch and narrow to the exact variant.

Example

interface Circle {
  kind: "circle";
  radius: number;
}
interface Square {
  kind: "square";
  sideLength: number;
}
type Shape = Circle | Square;
function calculateArea(shape: Shape) {
  switch (shape.kind) {
    case "circle": // TypeScript knows shape is Circle here
    return Math.PI * shape.radius ** 2;
    case "square": // TypeScript knows shape is Square here
    return shape.sideLength ** 2;
  }
}

The kind property is used as a discriminant to determine the type of the shape.

The in Operator

The in operator checks for the existence of a property on an object.

It's particularly useful for narrowing union types where different types have distinct properties.

Property Existence Checking

Narrow union members by testing whether a distinguishing property exists.

Example

interface Dog {
  bark(): void;
}
interface Cat {
  meow(): void;
}
function makeSound(animal: Dog | Cat) {
  if ("bark" in animal) {
    // TypeScript knows animal is Dog here animal.bark();
  } else {
  // TypeScript knows animal is Cat here animal.meow();
}
}

Type Assertion Functions

Type assertion functions are a special kind of type guard that can throw an error if the type assertion fails.

They're useful for validating data at runtime.

Assertion Functions

Encode runtime checks that narrow types and throw on invalid input.

Example

// Type assertion function function assertIsString(value: unknown): asserts value is string {
if (typeof value !== 'string') {
  throw new Error('Value is not a string');
}
}
// Type assertion function with custom error function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
  throw new Error(message);
}
}
// Usage function processInput(input: unknown) { assertIsString(input); // input is now typed as string console.log(input.toUpperCase());
}
// With custom error function processNumber(value: unknown): number { assert(typeof value === 'number', 'Value must be a number'); // value is now typed as number
return value * 2;
}

When to Use Each Type Guard

  • Use typeof for primitive types (string, number, boolean, etc.)
  • Use instanceof for class instances and built-in objects
  • Use user-defined type guards for complex validation logic
  • Use discriminated unions for related types with a common discriminant
  • Use the in operator for checking property existence
  • Use type assertion functions for runtime validation with errors

Performance Considerations

  • typeof and instanceof are very fast
  • Avoid complex logic in user-defined type guards when performance is critical
  • Consider using type predicates for expensive checks that are used multiple times

Previous

TypeScript Advanced Types

Next

TypeScript Conditional Types

This chapter

Overview
40

Lessons

241m

Read time

1. TypeScript Tutorial2. TypeScript Introduction3. TypeScript Getting Started4. TypeScript Simple Types5. TypeScript Explicit Types and Inference6. TypeScript Special Types7. TypeScript Arrays8. TypeScript Tuples9. TypeScript Object Types10. TypeScript Enums11. TypeScript Type Aliases and Interfaces12. TypeScript Union Types13. TypeScript Functions14. TypeScript Casting15. TypeScript Classes16. TypeScript Basic Generics17. TypeScript Utility Types18. TypeScript Keyof19. TypeScript Null & Undefined20. TypeScript Definitely Typed21. TypeScript 5.x Updates22. TypeScript Configuration23. TypeScript with Node.js24. TypeScript with React25. TypeScript Tooling26. TypeScript Advanced Types27. TypeScript Type Guards28. TypeScript Conditional Types29. TypeScript Mapped Types30. TypeScript Type Inference31. TypeScript Literal Types32. TypeScript Namespaces33. TypeScript Index Signatures34. TypeScript Declaration Merging35. TypeScript Async Programming36. TypeScript Decorators37. TypeScript in JavaScript Projects (JSDoc)38. TypeScript Migration Guide39. TypeScript Error Handling40. TypeScript Best Practices

On this page

Understanding Type Guards in TypeScriptWhy Use Type Guards?Type Guard Patternstypeof Type GuardsBasic Usageinstanceof Type GuardsClass-based Type GuardingUser-Defined Type GuardsType Predicate FunctionsDiscriminated UnionsBasic Discriminated UnionThe in OperatorProperty Existence CheckingType Assertion FunctionsAssertion FunctionsWhen to Use Each Type GuardPerformance Considerations