The satisfies operator — Learn TypeScript training

The satisfies operator added in version 4.9 of TypeScript helps ensure that an expression matches a type, while maintaining a type specific to the expression.

Let’s take a concrete example to better understand the situation.

type Colors = Record<string, [number, number, number] | string>

function demo (c: Colors) {}

We create a variable that corresponds to the signature and we try to send it to the function

const colors = {
  blue: [0, 0, 255],
  red: '#FF0000',
  green:  [0, 255, 0]
}

demo(colors) // Erreur, le type inféré pour "colors" ne correspond pas à "Colors"

The problem is that TypeScript will infer a type for that variable that doesn’t match our definition and it will return an error. The solution used so far was to force the type via a as or by defining the type of the variable.

const colors = {
  blue: [0, 0, 255],
  red: '#FF0000',
  green:  [0, 255, 0]
} as Colors

demo(colors) // Ok

This fixes our problem but causes another one. We lose the specificity of our variable

const colors = {
  blue: [0, 0, 255],
  red: '#FF0000',
  green:  [0, 255, 0]
} as Colors

colors.red.toLowerCase() // Property toLowerCase() does not exists on [number, number, number]

Our variable having taken the type of Colorsvalues ​​are for TypeScript of type string | [number, number, number] which does not correspond to what is written and that is where satisfies intervene!

const colors = {
  blue: [0, 0, 255],
  red: '#FF0000',
  green:  [0, 255, 0]
} satisfies Colors

colors.red.toLowerCase() // Ok
demo(colors) // Ok

Using this instruction rather than converting the type of colors in Colors TypeScript will infer a type that matches the requested type while keeping the specificity of our variable. Internally, the inferred type for colors will be:

type _ = {
    blue: [number, number, number],
    red: string,
    green: [number, number, number]
}

We correspond well to Colors while keeping a definition that matches the value of our variable.

When to use as ?

With this new operator one can wonder if as still has an interest. In my opinion as remains interesting to type an array before filling it for example.

const a = [] satisfies string[] // Renvoie un type never[] dans la version 4.9 de TS
const a = [] as string[] // Renvoie bien string[]
const a:string[] = []    // Equivalent au as 

But also for an empty object that we would like to initialize like Record<string, ????>

type Colors = Record<string, [number, number, number] | string>

const a = {} satisfies Colors 
a.red = '#FF0000' // Property "red" does not exists on type {}

const b = {} as Colors 
b.red = '#FF0000' // Ok