@satisfies {Type} checks that a value fits Type without changing the value's type. A misspelled or missing property is an error, as with @type, but the variable keeps the exact type TypeScript infers: its own keys, its literal values, its specific property types. TypeScript 5.0 added the tag as the JavaScript form of the satisfies operator from TypeScript 4.9.
That makes it the right tag for configuration objects, route tables, lookup maps and other values whose keys you want both checked and remembered.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| On a declaration | /** @satisfies {Record<string, Route>} */
const routes = { … }; | const routes = { … } satisfies Record<string, Route>; |
| On an expression | /** @satisfies {Route} */ ({ path: "/" }) | ({ path: "/" }) satisfies Route |
Examples
/** @typedef {{ path: string, auth?: boolean }} Route */
/** @satisfies {Record<string, Route>} */
export const routes = {
home: { path: "/" },
cart: { path: "/cart" },
account: { path: "/account", auth: true },
};
// The keys stay known: routes.home exists, routes.hmoe would be an error.
routes.home.path;type Route = { path: string; auth?: boolean };
export const routes = {
home: { path: "/" },
cart: { path: "/cart" },
account: { path: "/account", auth: true },
} satisfies Record<string, Route>;
routes.home.path;jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ path: string, auth?: boolean }} Route */
/** @satisfies {Record<string, Route>} */
export const routes = {
home: { path: "/" },
account: { path: "/account", atuh: true },error TS2353: Object literal may only specify known properties, and 'atuh' does not exist in type 'Route'.
};jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ path: string, auth?: boolean }} Route */
/** @type {Record<string, Route>} */
const typed = { home: { path: "/" } };
/** @satisfies {Record<string, Route>} */
const checked = { home: { path: "/" } };
// With @type, any key is allowed, so the typo is not caught.
typed.hmoe;
checked.hmoe;error TS2339: Property 'hmoe' does not exist on type '{ home: { path: string; }; }'.
export { typed, checked };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} @satisfies or @type?
@type {Record<string, Route>} makes the variable that type. Every key is then allowed, so typed.hmoe passes even though nothing defines it. @satisfies checks the same thing but leaves the type as { home: … }, so checked.hmoe is an error (TS2339). The third example puts the two side by side.
Use @type when the variable should accept other values of the type later, such as a let that is reassigned. Use @satisfies when the value is fixed and you want TypeScript to remember exactly what it is.
On functions
@satisfies also suits functions that must match a type from a library. Unlike @type, it does not impose the type's return type on the function body, which avoids the TS2355 trap described on the @type page and in the Express handler recipe.
Gotchas
- The
satisfiesoperator itself is TypeScript syntax;{…} satisfies Routein a.jsfile is TS8037. @satisfieschecks; it does not cast. To change a value's type, use a@typecast with parentheses.
Questions
What does @satisfies do in JSDoc?
It checks the value against the type in braces and reports any mismatch, while the variable keeps the narrower type TypeScript infers from the value itself.
Sources
- TypeScript 5.0: @satisfies support in JSDoc · TypeScript release notes
- TypeScript 4.9: the satisfies operator · TypeScript release notes
- JSDoc Reference: @satisfies · TypeScript handbook
Last reviewed 20 October 2026.