There are three ways to connect a value to a type, and they behave differently. @type {Settings} on a declaration annotates it: the variable has type Settings, and the value is checked against it. A cast, /** @type {Settings} */ (value), claims the type: TypeScript checks only that the two types overlap, so missing properties pass. @satisfies {Settings} checks the value like an annotation but leaves the variable with the value's own, narrower type.
The first example puts all three side by side. The cast accepts an object without pageSize, and only the satisfied object still knows that theme is exactly "dark". The @satisfies tag needs TypeScript 5.0.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| Annotate: the variable has the type | /** @type {Settings} */
const a = { … }; | const a: Settings = { … }; |
| Cast: claim the type | const b = /** @type {Settings} */ ({ … }); | const b = { … } as Settings; |
| Satisfy: check, keep the value's type | /** @satisfies {Settings} */
const c = { … }; | const c = { … } satisfies Settings; |
Examples
/** @typedef {{ theme: "light" | "dark", pageSize: number }} Settings */
/** @type {Settings} */
const annotated = { theme: "dark", pageSize: 24 };
// The cast is accepted although pageSize is missing: a cast claims, it does not check.
const cast = /** @type {Settings} */ ({ theme: "dark" });
/** @satisfies {Settings} */
const checked = { theme: "dark", pageSize: 24 };
/** @type {"dark"} */
const fromChecked = checked.theme;
/** @type {"dark"} */
const fromAnnotated = annotated.theme;error TS2322: Type '"light" | "dark"' is not assignable to type '"dark"'.
Type '"light"' is not assignable to type '"dark"'.
export { cast, fromChecked, fromAnnotated };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ theme: "light" | "dark", pageSize: number }} Settings */
const settings = { theme: "dark", pageSize: 24 } satisfies Settings;error TS8037: Type satisfaction expressions can only be used in TypeScript files.
export { settings };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Choosing one
Annotate variables that will be reassigned or passed around as the general type, and function parameters. Satisfy fixed values whose details matter later: configuration, lookup tables, route maps. Cast only when you know more than TypeScript can, such as the element behind a selector, and keep the cast next to the code that makes it true.
Gotchas
- The
satisfiesoperator is TypeScript syntax and TS8037 in a.jsfile, as the second example shows; use the tag. @satisfiesdoes not widen: if you later need the general type, annotate a second variable with@type.
Questions
When should I use @satisfies instead of @type?
When the value is fixed and you want TypeScript to remember its exact keys and literal values while still checking it against the type.
Sources
- TypeScript 5.0: @satisfies support in JSDoc · TypeScript release notes
- TypeScript 4.9: the satisfies operator · TypeScript release notes
- Everyday Types: type assertions · TypeScript handbook
Last reviewed 20 October 2026.