satisfies: Enforce Shape Without Losing Literal Types

Use the satisfies operator to validate objects (routes, feature flags, configs) while preserving literals for great inference and fewer casts.

F

Frontend Interview Team

March 01, 2026

2 min read
satisfies: Enforce Shape Without Losing Literal Types

What you’ll learn

  • Why as const is often not enough
  • How satisfies enforces a contract without widening your values
  • Real patterns: route tables, feature flags, and event maps

30‑second interview answer

satisfies checks that a value conforms to a type, but it keeps the value’s most specific inferred type. That makes it ideal for config objects where you want compile-time validation without losing literal types. Compared to const x: SomeType = ..., satisfies avoids unwanted widening.


The problem: annotation widens your literals

type RouteTable = Record<string, { path: string; auth: "public" | "private" }>;
 
const routes: RouteTable = {
  home: { path: "/", auth: "public" },
  settings: { path: "/settings", auth: "private" }
};
 
routes.home.path; // string (widened)

Sometimes you want "/" as a literal type.


satisfies keeps literals while enforcing the contract

type RouteTable = Record<string, { path: `/${string}`; auth: "public" | "private" }>;
 
const routes = {
  home: { path: "/", auth: "public" },
  settings: { path: "/settings", auth: "private" }
} satisfies RouteTable;
 
routes.home.path; // "/" (literal!)

You get:

  • Contract enforcement (path must start with /)
  • Literal inference for downstream typing

Pattern: route helpers with perfect inference

type Routes = typeof routes;
type RouteName = keyof Routes;
 
function linkTo<R extends RouteName>(name: R) {
  return routes[name].path;
}
 
const a = linkTo("home"); // "/"
const b = linkTo("settings"); // "/settings"

Pattern: feature flags with safe keys

type Flag = { owner: "growth" | "core"; rollout: 0 | 10 | 50 | 100 };
 
type FlagMap = Record<string, Flag>;
 
const flags = {
  newNav: { owner: "growth", rollout: 10 },
  fasterSearch: { owner: "core", rollout: 50 }
} satisfies FlagMap;
 
type FlagName = keyof typeof flags;

Now FlagName is a safe union: "newNav" | "fasterSearch".


Common mistakes

  • Using as SomeType to “force” a config (hides real errors)
  • Overusing as const everywhere instead of using satisfies strategically

Production rule of thumb

Use satisfies for config objects and lookup tables:

  • Routes
  • Events
  • Permissions
  • Feature flags

It gives you strict validation + great inference.


Interview questions

  1. Q: How is satisfies different from a type annotation?

    • A: Annotation sets the variable’s type (often widening). satisfies only checks compatibility and keeps narrow inference.
  2. Q: When is it most useful?

    • A: Config/lookup objects where you want both validation and literal types.

Quick recap

  • satisfies validates shape but preserves literals.
  • It’s perfect for route tables/config.
  • Prefer it over casts for safer, cleaner code.