TypeScript satisfies Operator
last modified October 15, 2026
The satisfies operator, added in TypeScript 4.9,
checks that an expression matches a type without changing the type of the
expression. A type annotation also checks the value, but replaces its
inferred type with the annotated one, which often loses useful information.
The satisfies operator gives both: validation and the precise
inferred type.
The problem with annotations
We define a palette of colors. Some colors are strings, some are RGB tuples.
type Color = string | [number, number, number];
const palette: Record<string, Color> = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
};
console.log(palette.green.toUpperCase());
The annotation makes sure that every value is a valid
Color. But it also makes the type of each property
Color, so the compiler no longer knows that
green is a string.
$ npx tsc --noEmit annotation.ts(9,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'. Property 'toUpperCase' does not exist on type '[number, number, number]'.
Using satisfies
With satisfies, the object is still checked against
the type, but the variable keeps the inferred type.
type Color = string | [number, number, number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
} satisfies Record<string, Color>;
console.log(palette.green.toUpperCase());
console.log(palette.red.map(c => c / 255));
The type of palette.green is string and
the type of palette.red is
[number, number, number], so string and array methods are
available.
$ npx tsx satisfies.ts #00FF00 [ 1, 0, 0 ]
Catching errors
The value is still validated. A wrong value is reported at the place where it is defined.
type Color = string | [number, number, number];
const palette = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0],
} satisfies Record<string, Color>;
The blue tuple has only two elements, so it is not a
valid Color.
$ npx tsc --noEmit
invalid.ts(6,5): error TS2322: Type '[number, number]' is not assignable to type 'Color'.
Type '[number, number]' is not assignable to type '[number, number, number]'.
Source has 2 element(s) but target requires 3.
Checking keys
When the keys are given as a union type, satisfies
reports both missing keys and misspelled ones.
type Env = "development" | "test" | "production";
const ports = {
development: 3000,
test: 3001,
prodution: 80,
} satisfies Record<Env, number>;
The compiler points out the unknown prodution key.
Once it is corrected, a missing environment would be reported as well.
$ npx tsc --noEmit keys.ts(6,5): error TS2561: Object literal may only specify known properties, but 'prodution' does not exist in type 'Record<Env, number>'. Did you mean to write 'production'?
Keeping literal types
Combined with as const, satisfies
validates an object and keeps the exact literal values of its
properties.
type Route = {
path: string;
method: "GET" | "POST";
};
const routes = {
users: { path: "/users", method: "GET" },
login: { path: "/login", method: "POST" },
} as const satisfies Record<string, Route>;
type RouteName = keyof typeof routes;
function describe(name: RouteName): string {
const route = routes[name];
return `${route.method} ${route.path}`;
}
console.log(describe("users"));
console.log(describe("login"));
The RouteName type is "users" | "login",
derived from the object itself. With a Record<string, Route>
annotation, it would be just string, and any name would be
accepted.
$ npx tsx routes.ts GET /users POST /login
satisfies versus type assertions
A type assertion with as also keeps the expression in
place, but it does not check the value: it only tells the compiler to treat
it as the given type.
type User = {
name: string;
age: number;
};
const asserted = { name: "Alice" } as User;
console.log(asserted.age);
The object has no age property, yet the assertion
compiles and asserted.age is undefined at
runtime.
$ npx tsx versus_as.ts undefined
type User = {
name: string;
age: number;
};
const checked = { name: "Alice" } satisfies User;
With satisfies, the missing property is reported.
$ npx tsc --noEmit
versus_satisfies.ts(6,35): error TS1360: Type '{ name: string; }' does not satisfy the expected type 'User'.
Property 'age' is missing in type '{ name: string; }' but required in type 'User'.
Function return values
The operator can be used on any expression, for example on the object returned from a function.
type ApiResponse = {
status: number;
data: unknown;
};
function okResponse(items: string[]) {
return {
status: 200,
data: items,
} satisfies ApiResponse;
}
const response = okResponse(["a", "b"]);
console.log(response.status, response.data.length);
The returned object must match ApiResponse, but the
inferred return type keeps data as string[] instead
of unknown, so length can be used. Like an
annotation, satisfies also reports unknown properties in object
literals, so a misspelled stauts key would be an error.
$ npx tsx return_value.ts 200 2
When to use what
- Type annotation (
const x: T = ...): the variable should have exactly the typeT, for example when it will be reassigned with other values of that type. - satisfies (
... satisfies T): the value must matchT, but the more precise inferred type is needed later. - Type assertion (
... as T): the developer knows more than the compiler; nothing is checked, so it should be rare.
Best Practices
- Use satisfies for Configuration: Objects with settings, routes, or lookup tables benefit from validation and precise keys.
- Combine with as const: Use
as const satisfies Tto keep literal values and check them at the same time. - Prefer satisfies to as: When the goal is to check a value,
satisfiesreports errors that an assertion would hide. - Keep Annotations for Variables: If a variable is reassigned, an annotation describes its type better.
- Mind the Version: The operator requires TypeScript 4.9 or later.
Source
TypeScript 4.9 release notes: The satisfies operator
This tutorial covered the TypeScript satisfies
operator. Use it when a value must match a type, but its own, more precise
type is still needed.
Author
List all TypeScript tutorials.