value satisfies Type checks that value matches Type at compile time, and then leaves the value's own, more precise type alone. An annotation would replace that precise type with Type; satisfies validates without widening.
satisfies still does the checking: a missing color, a misspelled key such as bleu, or a value like true is a compile error on that line. It exists since TypeScript 4.9 and, like every type annotation, it is removed from the emitted JavaScript.
The Problem satisfies Solves
With a type annotation, the variable's type is the annotation. The compiler forgets what it saw in the literal. Here the same palette is annotated instead, and now TypeScript no longer knows that green is a string:
The compiler reports:
index.ts(11,27): error TS2339: Property 'toUpperCase' does not exist on type 'Color'.
Property 'toUpperCase' does not exist on type '[number, number, number]'.
Before TypeScript 4.9 the choices were: annotate and narrow by hand everywhere (typeof palette.green === "string"), or skip the annotation and lose the check. satisfies gives both. Change : Record<ColorName, Color> to satisfies Record<ColorName, Color> after the closing brace and it runs.
satisfies vs Type Annotation vs as
The same settings object, written three ways:
as let the missing lang through, and asserted.lang is undefined at run time while its type says string. Remove lang from the other two lines and both fail with TS2741, Property 'lang' is missing in type ....
Annotation const x: T = v | Assertion v as T | v satisfies T | |
|---|---|---|---|
| Missing properties | error | allowed | error |
| Extra properties (object literal) | error | allowed | error |
| Wrong property type | error | only if the types do not overlap | error |
Type of x afterwards | T | T | the inferred type of v |
Literal types ("dark", 8080) | widened to T | widened to T | kept where T allows them |
Keys of a Record<string, ...> | any string (typos compile) | any string | exactly the keys written |
| Runtime effect | none | none | none |
Rule of thumb: annotate when you want the variable to have the declared type (a value you will reassign, a public API), and use satisfies when you want a check but the value's own type is more useful.
Catching Mistakes in Object Literals
satisfies runs the full assignability check, including excess property checks, so typos in keys are errors:
type Route = { path: string; method: "GET" | "POST" };
const home = { path: "/", metod: "GET" } satisfies Route;
// error TS2561: Object literal may only specify known properties, but 'metod' does not exist in type 'Route'. Did you mean to write 'method'?
The check also gives the literal a contextual type, just as an annotation does. That matters in two ways. String literals are kept as literal types when the target type expects them: { path: "/", method: "GET" } satisfies Route has method: "GET", while the same object with no annotation would infer method: string. And callback parameters are inferred from the target type:
Record Keys Stay Known
A common use is a lookup table. Annotated as Record<string, T>, every string is a valid key and a typo compiles, returning undefined at run time. With satisfies, the values are still checked against T, but the variable's type lists exactly the keys you wrote:
keyof typeof endpoints is only useful because the keys survived. With the annotation it would be plain string.
To require a fixed set of keys, satisfy a Record over a union: satisfies Record<"dev" | "prod", string> reports a missing prod with TS2741 and an unknown staging with TS2353.
as const satisfies
as const and satisfies combine. Write as const first: it makes the value deeply readonly with literal types, then satisfies checks that exact value.
Each route is checked against Route (a method: "PUT" would be an error), and the tuple of literal types stays available, so Path is a union of the real paths. Use readonly Route[] (or ReadonlyArray<Route>) as the target, since an as const array is readonly.
Config Objects
Configuration is where satisfies earns its place: the shape must be right, and code elsewhere wants the precise values.
Forget the production entry, misspell logLevel, or write logLevel: "verbose", and the compiler points at the exact line. The same pattern suits *.config.ts files: export default { ... } satisfies SomeConfig checks the whole file while the exported object keeps its literal values.
When Not to Use satisfies
- The variable will be reassigned.
let cfg = { port: 3000 } satisfies { port: number | string }givescfgthe type{ port: number }, so a latercfg = { port: "80" }fails (TS2322). Annotate variables you mean to change. - You want the declared type on purpose. For a function return value or an exported constant that is part of an API, the annotation's type is the contract, and leaking the exact literal type can make later changes breaking.
- The value is not a literal.
satisfiesshines on object and array literals. On a variable or a call result it is a plain assignability check, which an annotation already gives you.
Frequently Asked Questions
What does satisfies do in TypeScript?
expression satisfies Type checks at compile time that the expression is assignable to Type, reporting missing properties, extra properties and wrong value types, and then leaves the expression's own inferred type unchanged. You get the safety of an annotation and the precision of inference. It is erased from the JavaScript output.
What is the difference between satisfies and a type annotation?
Both check the value. An annotation (const x: T = ...) then gives the variable the type T, forgetting what the compiler knew about the value (literal types, which union member each property is, which keys exist). satisfies T keeps the inferred type, so x.someKey is known to exist and a string | number property that holds a string is typed string.
What is the difference between satisfies and as in TypeScript?
as is an assertion: it overrides the type and checks almost nothing, so missing properties go unnoticed. satisfies is a check: the value must really match the type, and its own inferred type is kept. When both would compile, satisfies is the safer choice.
What does as const satisfies mean?
It applies both: as const makes the value deeply readonly with literal types, then satisfies checks that result against a type. Write as const first: const routes = [...] as const satisfies readonly Route[];. The variable keeps the exact literal types for later use, and a wrong entry is still a compile error.
Which TypeScript version added satisfies?
TypeScript 4.9, released in November 2022. It is plain erasable syntax, so it also runs under Node's built-in type stripping, and every current TypeScript version (including 7) supports it.