Skip to main content

Archive

Show more

Creating Custom Type Guards in TypeScript

Creating Custom Type Guards in TypeScript

Custom type guards in TypeScript are user-defined functions that help narrow down types within conditional statements. They provide a way to refine the type of a variable based on custom logic, enhancing type safety and code clarity.


What are Custom Type Guards?

Custom type guards are functions that return a boolean value and use TypeScript's type predicates to specify the type of a variable. They are used to perform more complex type checking beyond the basic type guards provided by TypeScript.


Defining a Custom Type Guard

A custom type guard is defined by creating a function that includes a type predicate in its return type. This type predicate indicates which type the variable is refined to within the scope of the function.


function isString(value: any): value is string {
  return typeof value === "string";
}

In this example, the isString function is a custom type guard that checks if the given value is of type string. The return type value is string is a type predicate that tells TypeScript that value will be a string if the function returns true.


Using Custom Type Guards

Once you have defined a custom type guard, you can use it in your code to narrow down types within conditional statements:


function print(value: string | number) {
  if (isString(value)) {
    console.log(`String value: ${value}`);
  } else {
    console.log(`Number value: ${value}`);
  }
}

print("Hello"); // Output: String value: Hello
print(42);      // Output: Number value: 42

Here, the print function uses the isString type guard to differentiate between string and number types, allowing for type-specific handling.


Complex Custom Type Guards

Custom type guards can also handle more complex type checks involving multiple properties or conditions. For example:


interface Cat {
  type: "cat";
  meow: () => void;
}

interface Dog {
  type: "dog";
  bark: () => void;
}

function isCat(animal: Cat | Dog): animal is Cat {
  return animal.type === "cat";
}

function makeSound(animal: Cat | Dog) {
  if (isCat(animal)) {
    animal.meow();
  } else {
    animal.bark();
  }
}

const myCat: Cat = { type: "cat", meow: () => console.log("Meow!") };
const myDog: Dog = { type: "dog", bark: () => console.log("Woof!") };

makeSound(myCat); // Output: Meow!
makeSound(myDog); // Output: Woof!

In this example, the isCat type guard checks the type property to determine if the animal is a Cat. The makeSound function then uses this type guard to call the appropriate method based on the animal type.


Conclusion

Creating custom type guards in TypeScript is a powerful technique for enhancing type safety and code maintainability. By defining functions with type predicates, you can implement complex type checks and ensure that your code handles different types correctly. Custom type guards enable more precise type narrowing and improve the robustness of your TypeScript applications.

Comments