State-of-the-art frontend development
Imagine you're faced with the following problem:
You have a type City
that can be either CityName
or CityCoordinates
:
type City = CityName | CityCoordinates;
CityName
is a string representing one of the specified cities:
type CityName = "New York" | "Mumbai" | "Lagos";
CityCoordinates
is an object with x
and y
properties denoting the coordinates of your city:
type CityCoordinates = {
x: number;
y: number;
};
And the User
type has birthCity
and currentCity
, both of which are of type City
:
type User = {
birthCity: City;
currentCity: City;
};
Then, you want to create a new variable user
that adheres to the User
type.
You want to assign "Mumbai"
to the birthCity
property (that's of the type CityName
) and { x: 6, y: 3 }
to the currentCity
property (that's of the type CityCoordinates
).
What you would normally do is something like this π
const user: User = {
birthCity: "Mumbai",
currentCity: { x: 6, y: 3 },
};
That's all well and good and, as of TypeScript 4.8 of less, the only way to make sure that the user
variable matches the shape of the User
type.
Now, say you want to make the birthCity
property of the user
variable uppercase.
If you were to do that, however, you'd make TypeScript upset and get an error:
// yields this TypeScript error π
// Property 'toUpperCase' does not exist on type 'City'.
// Property 'toUpperCase' does not exist on type 'CityCoordinates'.
user.birthCity.toUpperCase();
The way that TypeScript sees the birthCity
property is that it can be either CityName
or CityCoordinates
.
It doesn't "know" that you want the user
variable to have the CityName
type for the birthCity
and the CityCoordinates
for the currentCity
.
satisfies
to the rescueBefore the satisfies
keyword was introduced, there wasn't a good way to achieve this.
More specifically, it wasn't possible to both
Now that we've got the satisfies
keyword, this very thing is now possible!
The syntax is as follows π
const user = {
birthCity: "Mumbai",
currentCity: { x: 6, y: 3 },
} satisfies User
The inferred type of the user
variable is going to be π
{
birthCity: "Mumbai";
currentCity: {
x: number;
y: number;
}
}
This will enable us to access string
-specific methods (like .toUpperCase()
) on the birthCity
property and access the x
and y
number properties on the currentCity
variable π
// we can call `toUpperCase`
user.birthCity.toUpperCase();
// ...and we can access the `x` (or `y`) properties
// and call `number`-specific methods on them
user.currentCity.x.toFixed(2);
And, on top of that, if we were to assign a value that doesn't match the User
type to the user
variable, we would get protected from type errors ππ»
const user = {
// assigning "Beijing" yields this error π
// Type '"Beijing"' is not assignable to type 'City'
birthCity: "Beijing",
currentCity: { x: 6, y: 3 },
} satisfies User
const user = {
name: 'Amy'
} satisfies User
In conclusion, with satisfies
we can get the best of both worlds. Type safety saves us from runtime errors and powerful type inference gives us most specific types for free.
You can find the source code for this tutorial on Typescript Playground.
If you liked this tutorial, you can also check out and subscribe to my YouTube channel. Or/and subscribe to my newsletter to get notified when I publish new articles! π
Stay up to date with state-of-the-art frontend development.