What Are TypeScript Type Predicates?

TypeScript type predicates determine whether an object has a specific property, enabling type narrowing to make code more readable. Here’s how it works.

Published on Sep. 05, 2024
Developers reviewing code on laptop
Image: Shutterstock / Built In
Brand Studio Logo

In TypeScript, type predicates check if an object has a specific property or set of properties and allows TypeScript to narrow the type of the object. This type narrowing allows developers to make precise assertions about the types of variables in their code. 

TypeScript Type Predicate Syntax

TypeScript’s type predicate allows you to check if an object has a property and enables type narrowing to make your code more readable and precise. The syntax is as follows: 

function yourFunction(argument: unknown): argument is <type> {
   // Perform an evaluation
   // If evaluation pass then
   return true
}

If you've been working with TypeScript for a while, you know that type safety is one of the language’s core features. However, even with the best type system in place, there are scenarios where we need to make more precise assertions about the types of variables in our code. That’s where type narrowing comes in. 

Type narrowing allows us to take a type that is too broad and refine it to a more specific type. This is a critical concept because it can prevent errors and make our code more expressive and readable.

 

TypeScript Type Predicates Explained

Type predicate functions are functions that return a boolean value and have a type predicate as their return type. A type predicate is a type assertion that checks if an object has a specific property or set of properties. This allows TypeScript to narrow the type of an object based on the result of the type predicate.

Here is an example of a type predicate function:

function isString(x: unknown): x is string {
  return typeof x === 'string';
}

In this example, the type predicate function isString takes an argument x of type unknown, which could be any value. The function then checks if the typeof x is 'string', and returns true or false accordingly.

The return type of the function is x is string, which asserts that x is of type string if the function returns true.

More on TypeScriptPascal Case vs. Camel Case Explained

 

TypeScript Type Predicates in If Statements

You can use type predicate functions in conditional statements to narrow the type of a variable based on the result of the type predicate:

function reverseString(x: unknown){
  if (isString(x)) {
    return x.split('').reverse().join('');
  }
    return null
}

In this example, the function reverseString takes an argument x of type unknown, which could be any value. The function then calls the type predicate function isString to check if x is a string. If isString(x) returns true, then x is treated as a string and can be reversed using the split, reverse and join string methods.

 

Advantages and Disadvantages of TypeScript Type Predicates

Advantages of TypeScript Type Predicate Functions

Type predicate functions are a powerful tool for type narrowing in TypeScript, but like any technique, they have their pros and cons.

One of the major advantages of type predicate functions is that they provide a way to express complex type relationships in a readable and understandable way. They allow you to define custom functions that not only perform a specific task but also return a boolean value that tells TypeScript whether a variable is of a particular type. This can make your code more expressive and self-documenting.

They can also be useful when you need to perform dynamic type checks on an object. For example, imagine you have a function that takes an object as an argument, but you aren’t sure whether the object has a specific property. With a type predicate function, you can check for the presence of that property and narrow the type of the object to include that property.

 

Disadvantages of TypeScript Type Predicates

On the downside, type predicate functions can be more difficult to set up and use than conditional blocks. They require you to define custom functions and may require more code to get up and running.

Additionally, type predicate functions can add complexity to your code, making it harder to read and understand. This can be particularly true when working with a large codebase.

One of the biggest risks of using type predicates for type narrowing is the possibility of writing incorrect predicates that lead to unexpected or undesired type narrowing. This can result in runtime errors or unexpected behavior, which can be difficult to diagnose and fix.

It’s important to note that while type predicates can be a powerful tool for type narrowing, they aren’t the only way to achieve this. Another common approach is to use type assertions with the as keyword, which allows you to tell the TypeScript compiler to treat a value as a particular type, even if the compiler is not able to infer it.

However, type assertions also carry some risks. They can lead to runtime errors if the asserted type is incorrect, and can also make your code less type-safe by allowing you to bypass type checking. As with type predicates, it’s important to use type assertions judiciously and with a clear understanding of their risks and benefits.

In this scenario, type predicates and type assertions behave as a lie to the type system, where you as a developer say, “I know more about this type than the compiler,” and force the type to be the desired one: 

function isString(x: unknown): x is string {
  return typeof x === 'number';
}

The above example checks if x is a number, and if that is true, then the predicate says that the variable is a string. If you later use that type predicate, TypeScript assumes that the variable is a string and the type safety will be lost.

A tutorial on how to avoid errors with TypeScript type predicates. | Video: Web Dev Simplified

More on TypeScriptCreate React App and TypeScript: A Quick How-To

 

TypeScript Type Predicates Best Practices

To use type predicate functions effectively, it’s important to follow best practices and guidelines. Here are some tips for working with type predicate functions:

  • Define type predicate functions carefully and ensure they are properly typed.
  • Use clear and descriptive names for type predicate functions.
  • Use type predicate functions only when they’re appropriate and necessary.
  • Consider alternative approaches to type narrowing, such as conditional blocks or discriminated unions.
  • Use automated testing and code analysis tools to detect potential errors or inconsistencies in the program.

Type predicate functions are a powerful tool for type narrowing in TypeScript. By using type predicate functions, developers can make their code more expressive, easier to read and maintain and less error-prone. However, it’s important to understand the pros and cons of using type predicate functions, and to follow best practices and guidelines for working with them. By doing so, you can create more robust and reliable applications with TypeScript.

Frequently Asked Questions

TypeScript type predicates allow you to check if an object has a specific property or set of properties so that TypeScript can narrow the type of the object. This makes code more precise and readable. Here’s the syntax:

function yourFunction(argument: unknown): argument is <type> {
   // Perform an evaluation
   // If evaluation pass then
   return true
}

TypeScript type predicates can be used in conditional statements to narrow the type of a variable based on the result of the type predicate. For example:

function reverseString(x: unknown){
  if (isString(x)) {
    return x.split('').reverse().join('');
  }
    return null
}
Explore Job Matches.