| name | flpbalada-typescript-satisfies-operator |
| description | Guides proper usage of TypeScript's satisfies operator vs type annotations. Use this skill when deciding between type annotations (colon) and satisfies, validating object shapes while preserving literal types, or troubleshooting type inference issues. |
TypeScript: The satisfies Operator
Core Concept
The satisfies operator validates that an expression matches a type without changing the inferred type. This is different from type annotations (:) which widen the type.
Key insight from Matt Pocock:
- "When you use a colon, the type BEATS the value"
- "When you use
satisfies, the value BEATS the type"
Type Annotation vs Satisfies
type RoutingPathname = "/products" | "/cart" | "/checkout";
const url1: RoutingPathname = "/products";
const url2 = "/products" satisfies RoutingPathname;
const test1: "/products" = url1;
const test2: "/products" = url2;
Classic Use Case: Object Validation with Preserved Types
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];
const palette1: Record<Colors, string | RGB> = {
red: [255, 0, 0],
green: "#00ff00",
blue: [0, 0, 255],
};
palette1.green.toUpperCase();
const palette2 = {
red: [255, 0, 0],
green: "#00ff00",
bleu: [0, 0, 255],
} satisfies Record<Colors, string | RGB>;
palette2.green.toUpperCase();
When to Use What
| Annotation Style | Type vs Value | Use Case |
|---|
: Type (colon) | Type wins | Need wider type for reassignment |
satisfies Type | Value wins | Need validation + narrow inference |
as Type | Lies to TS | Escape hatch (use sparingly!) |
| No annotation | Inference | Most common - let TS infer |
Rule of Thumb
Use satisfies when:
- You want the EXACT type of the variable, not the wider type
- The type is complex enough that you want validation you didn't mess it up
Use colon annotation when:
- You need to reassign the variable later with different values of the union
- You explicitly want the wider type
Common Pattern: as const satisfies
Combine as const for immutability with satisfies for validation:
const routes = {
home: "/",
products: "/products",
cart: "/cart",
} as const satisfies Record<string, string>;
Real-World Examples
Configuration Objects
type Config = {
api: string;
timeout: number;
retries: number;
};
const config = {
api: "https://api.example.com",
timeout: 5000,
retries: 3,
} satisfies Config;
Event Handlers Map
type EventMap = Record<string, (...args: unknown[]) => void>;
const handlers = {
click: (x: number, y: number) => console.log(x, y),
submit: (data: FormData) => console.log(data),
} satisfies EventMap;
Exhaustive Checks with Records
type Status = "pending" | "approved" | "rejected";
const statusLabels = {
pending: "Waiting for review",
approved: "Approved",
rejected: "Rejected",
} satisfies Record<Status, string>;
References