Advanced Patterns

satisfies operator

The satisfies operator validates that a value matches a type without widening the value's inferred type, preserving literal types and specific structure.

Definition

TypeScript
const config = {
  width: 100,
  color: "red",
} satisfies Record<string, string | number>;

Examples

Type Validation Without Widening
type Color = "red" | "green" | "blue";
type Config = Record<string, Color | Color[]>;

const palette = {
  primary: "red",
  secondary: "green",
  accents: ["blue", "green"],
} satisfies Config;

// palette.primary is "red" (not Color)
// palette.accents is ("blue" | "green")[] with array methods
palette.accents.map((c) => c.toUpperCase());
Exhaustive Route Map
type Route = "/" | "/about" | "/contact";

const routes = {
  "/": { title: "Home", component: "HomePage" },
  "/about": { title: "About", component: "AboutPage" },
  "/contact": { title: "Contact", component: "ContactPage" },
} satisfies Record<Route, { title: string; component: string }>;

// routes["/"].title is "Home" (literal), not string
console.log(routes["/"].title);
Config with Exact Types
interface DatabaseConfig {
  host: string;
  port: number;
  database: string;
  ssl?: boolean;
}

const dbConfig = {
  host: "localhost",
  port: 5432,
  database: "myapp",
  ssl: true,
} satisfies DatabaseConfig;

// dbConfig.port is 5432 (literal number type)
// dbConfig.host is "localhost" (literal string type)

Common Use Cases

  • 1Validating object shapes while preserving literal types
  • 2Ensuring exhaustive mappings without type widening
  • 3Configuration objects checked against interfaces
  • 4Constants that should match a schema but keep specificity
  • 5Theme objects validated against a contract

Understanding satisfies operator

The satisfies operator, introduced in TypeScript 4.9, validates that a value conforms to a type without changing the inferred type of the value. This solves a long-standing tension in TypeScript between type safety and type precision.

Before satisfies, you had two options when defining a constant. You could use a type annotation (const x: Type = value), which validates the value against Type but widens the inferred type, losing literal precision. Or you could omit the annotation (const x = value), which preserves literal types but provides no validation against a contract.

satisfies gives you both: the value is checked against the type, but the inferred type retains all the specificity of the actual value. const palette = { primary: "red" } satisfies Record<string, Color> validates that "red" is a valid Color, but palette.primary has type "red" (not Color).

This is transformative for configuration objects. You want to ensure your config matches the expected shape (all required keys present, correct value types), but you also want downstream code to benefit from the exact values. With satisfies, routes["/"].title is "Home" (a literal), not string, while still being validated against the route schema.

Exhaustive mappings benefit hugely from satisfies. When mapping over a union type with Record, satisfies ensures every union member has an entry, while as const-like precision is preserved in the inferred type.

satisfies combines well with as const for maximum precision. The value gets validated against the type (catching errors) while maintaining readonly literal types throughout. This pattern is increasingly common in modern TypeScript codebases for defining type-safe constants.

Related Types

More Advanced Patterns

Explore TypeScript Types

Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.