What problem does `satisfies` solve that neither a type annotation nor `as` does? Show a config object where it matters.
Whether you can validate a value against a type while keeping its precise inferred type.
`satisfies` checks that a value conforms to a type WITHOUT widening the value's inferred type. The classic dilemma: annotate `const config: Record<string, string | number>` and you get validation but lose specifics (config.port is now `string | number`, and TS forgets which keys exist); omit the annotation and you keep precise types but get no guarantee the object matches the intended shape. `as` is worse — it asserts and can hide real mismatches. `satisfies` gives you both: TS verifies the literal is assignable to the constraint (catching typos and wrong value types at the definition site), then keeps the NARROW inferred type for everything downstream. So `const routes = {…} satisfies Record<string, Route>` still lets you read `routes.home` as a concrete `Route` (not an index-signature `Route | undefined`) and preserves the literal types of the values. It's the modern answer to typed config, palettes, action maps, and `as const`-style objects that also need to honor an interface. Rule of thumb: reach for `satisfies` whenever you want a compile-time guarantee about a value's shape but don't want to lose the exact type the literal would otherwise infer. Combine it with `as const` when you additionally need readonly/literal narrowing on top of the constraint check.
Typed config objects, color palettes, route tables, reducer action maps — anywhere you want validation AND precise inferred value types.