TIL — Typescript type guards and predicates

Photo by Lautaro Andreani on Unsplash

What are type guards?

In TypeScript, type guards are a way to provide additional type information at runtime, making your code more type-safe and reducing the possibility of runtime errors. They help ensure that you are working with the correct type of value, enabling you to perform operations specific to that type with confidence.

Let’s examine a clear example to understand how type guards work. In this example, we have a custom type Foo and a function isValidItem that serves as a type guard for the Foo type:

type Foo = 'bar' | 'baz';

const isValidItem = (x: any): x is Foo => {
return typeof x === 'string' && (x === 'bar' || x === 'baz');
};
const item: any = 'bar';
if (isValidItem(item)) {
item;
}

Let’s break down the example step-by-step:

  1. Defining a custom type: We define a custom type Foo as a union of two string literal types, ‘bar’ and ‘baz’: type Foo = 'bar' | 'baz';.
  2. Creating a type guard function: We declare the isValidItem function with a single parameter x of type any. The function uses a type predicate x is Foo to indicate that if it returns true, x is of type.
  3. Checking the type: The function body checks if x is a string and if it’s either ‘bar’ or ‘baz’. If either condition is met, it returns true, meaning that x can be considered of type Foo:
  4. Using the type guard: We use the isValidItem function as a type guard in the if statement: if (isValidItem(item)) { ... }. If the condition is true (i.e., isValidItem(item) returns true), TypeScript knows that within this block, the item variable is of type Foo.
  5. Safely using the variable: Inside the if block, we can safely use the item variable as a value of type Foo without any type errors.

Type Predicates: A Quick Overview

Type predicates are a special return type annotation in TypeScript, used to provide specific type information within a function. They play a crucial role in type guard functions, enabling the TypeScript compiler to narrow down the type of a variable when the function returns true.

A type predicate follows the syntax argName is TypeName, where argName is the function argument, and TypeName is the type the function checks for. This informs the compiler that, under certain conditions, the argument can be considered to be of the specified type.

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

Here, value is string is the type predicate, telling TypeScript that if isString returns true, the value argument is of type string. This allows the compiler to narrow down the variable type when using this function as a type guard.

Conclusion

In summary, type guards in TypeScript are a valuable tool for ensuring that variables are of the expected type within certain code blocks. This can help prevent type-related errors, making your code more robust and easier to maintain. In our example, the isValidItem function serves as a type guard, checking if a given value is of the Foo type and allowing us to safely use the variable within the if block.

,