Narrowing types using the in operator in TypeScript
— TypeScript — 2 min read
As part of Fullscript's design systems team, our most recent project has been implementing our new design tokens by changing all of the existing colour values in our component library to use our new light theme tokens. Some of the components in the component library are typed to a set of colour profiles to add a range of colours.
I recently ran into a typing issue where TypeScript was throwing an error as the profile token I used did not exist for all of the colour profiles typed. This was the kind of error I kept getting (names changed for context and easier explanation):
Property 'lettuce' does not exist on type 'Lunch'.
Property 'lettuce' does not exist on type 'Steak'.
Let's go back a bit. The type Lunch
is a union type, or a type made up of multiple types. Lunch
looks something like this:
type Burger = {
beef: Boolean,
bun: Boolean,
ketchup: Boolean,
mustard: Boolean,
lettuce: Boolean,
pickles: Boolean,
};
type Steak = {
beef: Boolean,
butter: Boolean,
garlic: Boolean,
fries: Boolean,
};
type Lunch = Burger | Steak;
I really want to access the lettuce
property in Lunch
, but because lettuce
is non-existent in the type Steak
, I can't access lettuce
if I've typed a variable to Lunch
since it doesn't exist in both Burger
and Steak
.
It turns out I can use JavaScript's in
operator to remove this TypeScript error and help me access the lettuce
property, even though it only exists in one of the types defined in Lunch
. The in
operator returns true
only if the property exists in the object specified or the prototype chain.
Accessing lettuce
this way no longer throws an error:
const whatsForLunch = (meal: Lunch) => {
if ("lettuce" in meal) return meal.lettuce;
return meal.beef;
}
And that's it! You can read more about how narrowing in TypeScript in the official docs.
If you have any questions or notice an error in this post, drop me a line at [email protected].