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 Colors
values 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