In their v4.9, the TypeScript team is releasing a new operator: satisfies
(see blog post https://devblogs.microsoft.com/typescript/announcing-typescript-4-9-beta/#the-satisfies-operator).
Purpose
The purpose of satisfies
is to enforce a constraint on a variable, without changing its type.
For instance, you want to say that a color is "either a string, or a RGB tuple", which would give something like that:
type RGB = readonly [red: number, green: number, blue: number];
type Color = { value: RGB | string };
const myColor: Color = { value 'red' };
But now, we don't know whether myColor.value
is a string or a tuple. So we cannot do something like myColor.value.toUpperCase()
(even if it's actually a string).
In TS 4.9, it'll be possible to do this (TypeScript Playground):
type RGB = readonly [red: number, green: number, blue: number];
type Color = { value: RGB | string };
const myColor = { value: 'red' } satisfies Color; // works
const myIncorrectColor = { value: 100 } satisfies Color; // throws error
myColor.value.toUpperCase(); // valid operation as myColor is a string
Combining as const
and satisfies
As expected, you combine as const
and satisfies
(TypeScript Playground).
type RGB = readonly [red: number, green: number, blue: number];
type Color = RGB | string;
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [1,2,3],
} satisfies Record<string, Color>;
console.log(palette.green);
// ^? green is string
const constantPalette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [1,2,3],
} as const satisfies Record<string, Color>;
console.log(constantPalette.green);
// ^? green is "#00ff00"
Note: the order matters. While as const satisfies <type>
works, the opposite isn't true: satisfies <type> as const
will throw a TS error (TypeScript Playground):
type RGB = readonly [red: number, green: number, blue: number];
type Color = RGB | string;
const invalidPalette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [1,2,3],
} satisfies Record<string, string | RGB> as const; // throws an error
Oldest comments (9)
type RGB = readonly [red: number, green: number, blue: number];
why is
readonly
used in this instance?what actually is the result type? an array of strings or some type of enum? eg
["red": 1, "green": 2]
could you explain more why:
so if
myColor
can be a string OR an RGB, how is it always going to work with the.toUpperCase
? Would that not fail on the tuple type of RGB?It's used because using
as const
on an array creates a readonly tuple. So I had to use it to make the 2 types compatible.That's the point of
satisfies
: it won't be a string or a RGB: when you doconst myColor = 'red'
,myColor
will have the typestring
.Adding
satisfies Color
doesn't change its type, but instead checks that it can be casted into aColor
. And indeed,string
can be casted (but won't be casted) as aRGB | string
.So as
myColor
stays a string, TS won't throw an error onmyColor.toUpperCase()
But hypothetically, then, you have an RGB type which satisfies the requirements for the Color type; what actually happens when you try to run
.toUpperCase()
?At the end of the day,
type Color = RGB | string
is really just saying that anything stored as aColor
should either be aRGB
or astring
. And, usingsatisfies
is supposed to put us in a position where we can be confident that it's safe to use string methods or methods specific to areadonly array
(RGB
) should work or have some standard behavior across allColor
instances. I don't really understand how that works or how that's supposed to be handled.As the types are stripped away in the runtime, it'd be regular JS:
becomes
so it'd just apply
toUpperCase()
to'red'
, returning'RED'
.satisfies
doesn't change the type ofmyColor
. It just checks that the type of the variable can be assimilated as another one. You can think about it as:It'd be a tuple of 3 numbers, something like:
Adding
readonly
will only tell TS that you cannot push / mutate its content. And thered
,green
,blue
is just to gave labels to the 3 values (hints on the values).See devblogs.microsoft.com/typescript/...
Hi. I think your example does not fully explain the need of
satisfies
. At least my VS Code & TypeScript compiler is happy with the following example:It also works on TypeScript Playground.
You're right, in this case TS is able to properly infer the type
string
. I had a more complex example in my previous draft, but I tried to make it simpler for the blog post (and I didn't try the new version 😬)Try with this piece of code:
In this case, TS will use the type
Color
forvalue
and notstring
.See playground
You can also try in TS 4.9
Cool, just wondering how this feature can be helpful, If there are two possible types for a property, so it's better the code handles both scenarios.
To me it's against the premise of TS even, unless I'm missing something.
This is actually quite nice and addresses a personal painpoint for me.