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
const config = {
width: 100,
color: "red",
} satisfies Record<string, string | number>;Examples
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());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);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
as const assertionThe as const assertion marks a value as deeply readonly with the most specific literal types possible, preventing type widening.
typeof (Type Operator)The typeof type operator extracts the TypeScript type from a value-level variable or expression, bridging the value and type worlds.
Record<K, T>Constructs an object type whose property keys are K and whose property values are T, useful for dictionaries and lookup maps.
keyof TThe keyof type operator produces a union of all known public property keys of a type, enabling type-safe property access patterns.
More Advanced Patterns
Explore TypeScript Types
Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.