Exploring the satisfies operator in TypeScript

Written by Samuel Martins✏️

A new addition to the TypeScript programming language as of v4.9, the satisfies operator makes it easier to write code that checks whether a value satisfies a type. Rather than checking whether it is of a specific type, sat…


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Matt Angelosanto

Written by Samuel Martins✏️

A new addition to the TypeScript programming language as of v4.9, the satisfies operator makes it easier to write code that checks whether a value satisfies a type. Rather than checking whether it is of a specific type, satisfies checks if a type satisfies the constraints of a generic type.

Don’t worry if you get a little bit confused in the beginning; consider the following simple example:

let x: any = "hello";
if (x satisfies string) {
    console.log("x is a string");
}

As you can see, the satisfies operator checks if the value of x is a string, and if it is, it logs a message to the console. Going by the example alone, you can already notice that the satisfies operator is a type guard. Therefore, it allows the TypeScript compiler to narrow the type of a value based on the result of the check, which can be useful for optimizing type checking in your code.

By the end of this article, you’ll understand what problem satisfies solves, how to use it in your code, and the benefits of using it in your codebase. Let’s get started!

Jump ahead

  • What is the satisfies keyword?
  • What problem does satisfies solve?
  • How does satisfies solve the type checking problem?
  • A more complex example of satisfies
  • Different ways to use satisfies in your code
  • Benefits of using satisfies in your code

What is the satisfies keyword?

The satisfies keyword in TypeScript is a type guard that allows you to check if a value matches a specific type. This is useful when working with complex data structures or when dealing with values that can be of multiple types.

The satisfies keyword is used in type constraints to indicate that a given type must be compatible with a certain interface or must extend a certain class. For example, if you have a function that takes a parameter of type T, you can use a type constraint to specify that T must be a class that extends the BaseWidget class, like this:

function createWidget<T extends BaseWidget>(widget: T): T {
    // The code for this function goes here
}

The type constraint T extends BaseWidget specifies that the type parameter T must be a class that extends the BaseWidget class. Therefore, any value passed to the createWidget function must be an object of a class that extends BaseWidget. Otherwise, the TypeScript compiler will throw an error. This is just a quick high level example; we’ll cover more examples later on.

What problem does satisfies solve?

TypeScript still works without satisfies, so you may be wondering exactly what problem it solves. Is it just solutionism at its worst? Well, one common problem that the satisfies keyword solves is type checking within conditional statements.

If you don't use the satisfies operator in your code, you won't be able to take advantage of its type-checking capabilities. Therefore, the TypeScript compiler won't be able to narrow the type of a value based on the result of a satisfies check, and you may end up with type errors or other problems in your code.

For example, let's say you have a variable x with the type any, which means that it can hold any type of value. Without using the satisfies operator, you might try to access a property on x that is only available on strings:

let x: any = "hello";
let y = x.toUpperCase(); 

In this case, the TypeScript compiler will generate an error because it doesn't know that the value of x is actually a string. However, if you use the satisfies operator, you can tell the compiler that the value of x is a string, and it will allow you to access the toUpperCase property:

let x: any = "hello";
if (x satisfies string) {
    let y = x.toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'
}

How does satisfies solve the type checking problem?

The satisfies keyword approaches this type checking problem by providing a concise and intuitive syntax for checking the type of a value. To use the satisfies keyword, you simply specify the type that you want to check for within the satisfies clause.

When the TypeScript compiler encounters a satisfies check in your code, it will evaluate the check and narrow the type of the value being checked based on the result. For example, if you have a variable x with the type any, and you check if it satisfies the type string, the compiler will narrow the type of x to string if the check evaluates to true.

Below is an example of how this works in practice. I’ll use the same example from above:

let x: any = "hello";
if (x satisfies string) {
    // At this point, the compiler knows that the type of x is 'string'
    let y = x.toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'
}

In this example, the satisfies check evaluates to true, so the compiler narrows the type of x to string. This allows you to access the toUpperCase property on x without getting a type error.

A more complex example of satisfies

So far, I’ve played it safe with the examples just to get your feet wet. You probably won’t use hello string types in your code, so consider the following, more realistic example:

type User = {
    id: number;
    name: string;
    email: string;
    isAdmin: boolean;
};

function getUserById(id: number): User | undefined {
    // Fetch user from the database...
    // For this example, we'll just return a hard-coded user object
    return {
        id: 1,
        name: "John Doe",
        email: "johndoe@example.com",
        isAdmin: false
    };
}

function getUserName(id: number): string | undefined {
    let user = getUserById(id);
    if (user satisfies User) {
        // At this point, the compiler knows that the type of user is 'User'
        return user.name;
    } else {
        return undefined;
    }
}

let userName = getUserName(1);
console.log(userName); // Output: 'John Doe'

In the example above, we have a getUserById function that fetches a user from the database by their ID. The function returns a User object if the user is found, or undefined if the user is not found.

We also have a getUserName function that takes a user ID as an argument, returns the user's name if the user is found, and returns undefined if the user is not found. Inside the getUserName function, we use the satisfies operator to check if the user variable is of type User, and if it is, we return the user's name.

Different ways to use satisfies in your code

Now that you know how satisfies works and how it approaches the type checking problem, we'll explore the different ways you can use it.

Narrowing the type of a value based on satisfies

let x: any = "hello";
if (x satisfies string) {
    // At this point, the compiler knows that the type of x is 'string'
    let y = x.toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'
}

In the code above, you can determine through inference that the compiler already knows that the type of is a string.

The if statement condition only checks if x fits the criteria by testing the toUpperCase method. If this variable was a number instead, the if statement would have returned false, and therefore, line 4 would not be reached. The same applies to the inline assertion and the type guard functions, below.

Using the satisfies operator in an inline type assertion

let x: any = "hello";
let y = (x as string).toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'

Using the satisfies operator in a type guard function

function isString(x: any): x is string {
    return x satisfies string;
}

let x: any = "hello";
if (isString(x)) {
    // At this point, the compiler knows that the type of x is 'string'
    let y = x.toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'
}

Using the satisfies operator in a type predicate

type IsStringOrNumber<T> = T extends string | number ? T : never;

function isStringOrNumber<T>(x: T): x is IsStringOrNumber<T> {
    return x satisfies string || x satisfies number;
}

let x: any = "hello";
if (isStringOrNumber(x)) {
    // At this point, the compiler knows that the type of x is either 'string' or 'number'
    if (x satisfies string) {
        let y = x.toUpperCase(); // OK: property 'toUpperCase' exists on type 'string'
    } else {
        let y = x.toFixed(2); // OK: method 'toFixed' exists on type 'number'
    }
}

A type predicate is basically a function that takes in a value and returns a boolean indicating whether or not the value is of a certain type or satisfies a given condition.

Typically, these are used in conjunction with type guards. Consider the example above. The function defined on line 3 basically returns if whether x is of type string or number. When it is called on line 8, the compiler is already aware of its type depending on the value of x on line 7. That being said, the nested if statement is basically a type guard and executes based on what the condition returns.

In this case, line 11 runs; the definition of x on line 7 and the check on line 8 basically shows that x is a string.

Benefits of using satisfies in your code

There are several benefits of using the satisfies operator in your TypeScript codebase, some of which include improved type checking, better code organization, and enhanced code efficiency.

The satisfies operator allows the TypeScript compiler to narrow the type of a value based on the result of a check, which can help prevent type errors and improve the overall type-checking capabilities of your code.

By using the satisfies operator, you can organize your code into logical blocks based on the type of a value. This can make your code easier to read and more understandable, also helping to prevent repeating type checks in different parts of your code.

Finally, because the satisfies operator allows the compiler to narrow the type of a value, it can help make your code more efficient by reducing the amount of type checking that must be done at runtime, resulting in faster and more efficient code execution.

Conclusion

The satisfies operator offers a more concise and flexible way to check whether a value satisfies a given type. It is a valuable addition to TypeScript that makes it easier to write code that performs type checking. It allows for more precise and flexible type checking, and it can make your code simpler and more readable even when working with complex types hierarchies.

LogRocket: Full visibility into your web and mobile apps

LogRocket signup

LogRocket is a frontend application monitoring solution that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.

In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page and mobile apps.

Try it for free.


This content originally appeared on DEV Community 👩‍💻👨‍💻 and was authored by Matt Angelosanto


Print Share Comment Cite Upload Translate Updates
APA

Matt Angelosanto | Sciencx (2023-02-08T16:28:48+00:00) Exploring the satisfies operator in TypeScript. Retrieved from https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/

MLA
" » Exploring the satisfies operator in TypeScript." Matt Angelosanto | Sciencx - Wednesday February 8, 2023, https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/
HARVARD
Matt Angelosanto | Sciencx Wednesday February 8, 2023 » Exploring the satisfies operator in TypeScript., viewed ,<https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/>
VANCOUVER
Matt Angelosanto | Sciencx - » Exploring the satisfies operator in TypeScript. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/
CHICAGO
" » Exploring the satisfies operator in TypeScript." Matt Angelosanto | Sciencx - Accessed . https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/
IEEE
" » Exploring the satisfies operator in TypeScript." Matt Angelosanto | Sciencx [Online]. Available: https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/. [Accessed: ]
rf:citation
» Exploring the satisfies operator in TypeScript | Matt Angelosanto | Sciencx | https://www.scien.cx/2023/02/08/exploring-the-satisfies-operator-in-typescript/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.