The New Satisfies Operator Will Satisfy You (Cheesy)

The New Satisfies Operator Will Satisfy You (Cheesy)

I know the title of the post is cheesy ๐Ÿ˜› but let me assure you once you know what the satisfies operator does, you will get the context.

ยท

5 min read

TypeScript beta 4.9 came with the new satisfies operator. The satisfies operator aims to give the ability to the developers to assign the most specific types of expressions for inference. Let's see it in action.

TL;DR If you prefer watching a video to reading the article, you can watch the video on YouTube.

Let's Start With a Simple Object

Let's assume that we have a color object. The color object represents a color value in RGB that is red, green, and blue. The value of red, green, and blue can either be a string, a hex code representing a color like #00ff00, or an array of three numbers representing an array of RGB values. The three numbers in the array are again red, green, and blue values. The structure of the color object is a standard way of defining color values.

// Color Object
const color = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [225, 255, 0],
};

We want to have the ability to call array methods on each of the properties of the color object. So if I need to get the value of red at the index of 0, I should be able to get it using the bracket notion.

const redComponent = color.red[0]; // 255

// Here, the constant `redComponent` should be equal to  `255`.

Also, since the value of any of these properties can be a string, we would want the ability to use string methods on property values if it's a string. We can declare another constant, greenValue, and convert the value of the green color to upper case.

const greenValue = palette.green.toUpperCase(); // '#00FF00'

//  The constant `greenValue` should be converted to upperCase like so '#00FF00'

Making Our Object Type Safe

While trying to type the color object strictly, we must keep the above use cases in mind and ensure that the object's properties are not misspelled, which means red, green, and blue should always be spelled correctly. Typescript must throw an error if these properties are not spelled correctly.

To do this, we can declare a type called properties, which will be a union of red, green, and blue.

type Properties = 'red' | 'green' | 'blue';

For the values of each of these properties, we can declare a type called RGB, which is a tuple. A tuple is an array that takes only has three properties that are red, green, and blue. The values of each tuple property will be of the type number.

type RGB = [red: number, green: number, blue: number];

We can now use a Record type, a built-in utility type in TypeScript that constructs an object with Keys and values for those keys.

const color: Record<Properties, RGB | string> = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [225, 255, 0],
};

The record type here ensures that all the properties of the color object are of the type Properties and the values of the properties are a union of type RGB or string. So we cannot have a property that is not either red, green, or blue. So if I try to misspell blue, TypeScript will throw an error. Also, all the values assigned to these properties now either have to be a string or of the RGB.

The Problem With This Approach

We have only won half of the battle till now. But there is a problem here. Looking at the greenValue constant, we will see that TypeScript will throw an error. We get an error because TypeScript is unsure about the value of green as a string or a tuple, and we are trying to use a string method to convert a string to uppercase. If the value is not a string, it will result in an error. To use a string method, we would have to manually validate the property before we can use a string method like so:

if (typeof color.green === 'string') {
  const greenValue = color.green.toUpperCase();
}

Introducing The satisfies operator

For such situations, the new satisfies operator is super helpful. The satisfies operator pre-validates the object's properties for us. We can remove the type definition from the color object and use the satisfies operator.

// Color Object
const color = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [225, 255, 0],
} satisfies Record<Properties, RGB | string>;

Now the satisfies operator will validate the red, green, and blue values for us and check whether each of those properties contains a string or an array of RGB. So now, even if I remove the condition from the greenValue constant, TypeScript will not throw an error because it now knows that the property green contains a string and not an array. Since we used the satisfies operator, TypeScript pre-validates the values of the properties of the colors object.

const greenValue = color.green.toUpperCase();

Similarly, if I will now try to use a string method on the value of blue, Typescript will throw an error because it knows in advance that blue is an array of numbers and a string method cannot be applied to an array of numbers.

const blueValue = color.blue.toUpperCase();
// TypeScript Will throw an error

The satisfies operator does the heavy lifting for you and checks all the values so that you do not have to manually check each of the values of an object to be of a particular type before using a specific method on that value. If you want to learn TypeScript, you might be interested in taking my TypeScript Crash Course on YouTube or subscribing for more such updates and tutorials.