satisfies in practice
Three real cases where it earned its keep.
BasicSelf
The satisfies operator (TypeScript 4.9+) validates that a value matches a type without widening the type. Three real cases where it’s better than an annotation.
The Problem with Annotations
const palette: Record<string, string> = { primary: "#007bff", secondary: "#6c757d",};
palette.primary.toUpperCase(); // Error: string might not have toUpperCaseWait, string definitely has toUpperCase. The problem: annotating as Record<string, string> widens the type so TypeScript forgets the specific keys. You lose autocomplete on palette.primary.
Case 1: Preserve Literal Types
const palette = { primary: "#007bff", secondary: "#6c757d",} satisfies Record<string, string>;
palette.primary.toUpperCase(); // Works — TypeScript knows it's a stringpalette.nonexistent; // Error — TypeScript knows the exact keyssatisfies validates the shape but keeps the inferred type.
Case 2: Config Objects
type Config = { port: number; host: string; debug?: boolean;};
const config = { port: 3000, host: "localhost", extraField: true, // Error — not in Config} satisfies Config;
config.port; // TypeScript infers number, not just Config['port']Case 3: Tagged Union Members
type Action = { type: "INCREMENT"; amount: number } | { type: "RESET" };
const increment = { type: "INCREMENT", amount: 5,} satisfies Action;
increment.amount; // number — not lost to the union typesatisfies vs. as const vs. Annotation
| Approach | Validates shape | Preserves literal types |
|---|---|---|
| Annotation | Yes | No — widens |
as const |
No | Yes — too narrow |
satisfies |
Yes | Yes |
Why It Matters
satisfies fills a gap between “I want type safety” and “I want precise autocomplete.” It’s small but it removes the choice between validation and precision.