I have been using satisfies
wrong. And I can safely state that because every time I tried it, I never got it work correctly.
After growing frustrated a bit and thinking that it’s just another tool in the TypeScript toolkit, I did what every developer does: I forgot about it.
But there were always some cases where I tried to incorporate it. And it still didn’t work. It’s time to learn how satisfies
actually works and how my workflow could benefit from using it.
Prior mistakes
Before diving into the topic, I should describe how I used it. It will become clear why this did not work once we know, how to use satisfies
.
I always tried to use it as a replacement for as
. Let’s look at the following code:
In this case, I did not add the status
property to the invitees and with satisfies
I thought I could tell TypeScript hey, I know, there’s a property missing, but it’s okay, don’t worry! As
does work this way, so I thought this would be okay.
But apparently it is not okay. When using GraphQL, it might happen that you do not have to query every single property of a specific type, but you may have used the specific type in a different type definition. Coercing the queried values with as
can be a valid path to achieve this. And I was under the impression that satisfies
works the same.
Actual usage
The correct way of using satisfies
is not coercing types. It’s actually the opposite: you can further restrict the type than with “conventional” methods.
Let’s start with defining some types and a variable:
The best way to type this is actually to not type it at all. TypeScript is very good at inferring the type from the value. But for the sake of this article, we have to type it.
The first instinct would be to do the following:
Here two things happen:
- autocomplete disappears
- if you try to access a property that does not exist, it does not display an error
Trying routes.settings.path
should trigger a bad red squiggly, but it doesn’t. Next, we try to use as
instead:
That doesn’t look good. The complete variable gets a bad red squiggly (which would not happen with the first method). Imagine this variable is much larger, with more properties. It will take some time to find the erroneous property that needs to be fixed. And even if you fix the issue, the same two issues persist.
It’s time for satisfies
to enter the chat:
TypeScript now knows the properties of the routes
variable: autocomplete returns, accessing issues that do not exist will trigger TypeScript to start complaining. Nice! But it would be even better if routes.events.path
would not only be typed as string
but actually as /events
.
as const
+ satisfies
= superpower
There’s a way to achieve this: combining as const
and satisfies
.
This combination is so good that it even beats not typing the variable at all! If you just use as const
the path gets inferred correctly, but if you mess up a property, TypeScript won’t warn you. But using as const
with satisfies
feels like a hidden superpower.
Conclusion
Finally looking into the docs and learning how satisfies
works showed me, that my initial assumption was flat out wrong. Knowing how to use it now will certainly come in handy in tricky situation that normally would need larger amounts of TypeScript gymnastics.