adamhl.dev

TypeScript 'satisfies' Operator

2 min read

A quick overview of the 'satisfies' operator in TypeScript.


I recently discovered the TypeScript satisfies operator which solves a particular typing problem I’ve come across time and time again.

The satisfies Operator

In a nutshell, it ensures that something conforms to a specific type while also giving that thing the most specific (inferred) type.

Let’s say we have an object where the keys are strings and the values are objects. And we know that in the future we might add another section like section3.

const sections = {
section1: {
detail1: "",
},
section2: {
detail1: "",
},
}

Sometimes we might want a type that is the keys of our sections object.

type SectionNames = keyof typeof sections
// The actual inferred type looks like:
// type SectionNames = "section1" | "section2"

Cool, now we have type called SectionNames that is very specifically one of two string literals (section1 or section2).

But wait, we want the sections object to conform to a specific shape. Specifically, we want two things:

So we might do something like:

interface SectionDetails {
detail1: string
}
const sections: Record<string, SectionDetails> = {
section1: {
detail1: "",
},
section2: {
detail1: "",
},
}

This allows us to add as many sections as we want (without having to also add the same section key to some overall Sections interface) and each section must be an object with the specified key/value pairs.

There’s a problem though. Let’s look at what our SectionNames type looks like now:

type SectionNames = keyof typeof sections
// Now section names is:
// type SectionNames = string

Now SectionNames is just of type string.

Because we typed sections as Record<string, SectionDetails>, TypeScript is no longer inferring the more specific type.

So the question is, how can we ensure that sections conforms to the shape we want while also having our SectionNames type be the exact keys and not just string?

This is where the satisfies operator comes in. Instead of specifying the type of sections, we say it must satisfy some type:

const sections = {
section1: {
detail1: "",
},
section2: {
detail1: "",
},
} satisfies Record<string, SectionDetails>
// Still the specific inferred type!
// And sections must satisfy the defined type.
type SectionNames = keyof typeof sections
// type SectionNames = "section1" | "section2"