What happened
We had a route map written as an object, with the variable annotated as Record<string, Route>. The shape was checked, but autocomplete did not show the real keys like home or login, and a mistyped key did not error because the type allowed any string.
We wanted both: the compiler checking that every entry has the right shape, and the editor knowing which keys actually exist.
Why it happens
With a type annotation, the variable gets the type you wrote, not the type of the value you assigned. All that remains is the broad information in Record<string, Route>, and the specifics of the object you wrote are gone.
satisfies (available since TypeScript 4.9) checks the expression against a type without changing the variable's type. The inferred type still comes from the actual value, so the keys are exactly the ones you wrote, and properties that the target type declares as literal unions, such as 'GET' | 'POST', stay literal. If you want every value to be a literal, combine it with as const.
What we do now
Our config objects and route maps now use satisfies instead of an annotation. A mistyped key is flagged immediately, autocomplete lists only the real keys, and an entry with the wrong shape errors right at that entry.
When you deliberately want a wide variable, for example one that accepts many kinds of values, a normal annotation is still the right choice. satisfies fits objects whose contents you already know.
type Route = { path: string; method: 'GET' | 'POST' }
const routes = {
home: { path: '/', method: 'GET' },
login: { path: '/login', method: 'POST' },
} satisfies Record<string, Route>
routes.login.method // 'POST'
routes.logn // error: Property 'logn' does not existQuick checklist
- 1Remove the type annotation from the variable.
- 2Add satisfies followed by the type to check against at the end of the object.
- 3Try a mistyped key to confirm the compiler complains.
- 4If you need fully literal values, add as const before satisfies.
Rule of thumb
Use satisfies when you want the compiler to check a shape without widening the type of the object.