Why satisfies Exists
The gap between annotation and inference that satisfies fills.
Why satisfies Exists is a free TypeScript Academy lesson on CoddyKit — lesson 1 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
The Two Goals in Tension
When you declare a config, you usually want two things: validation against a known shape, and precise inference of the literal values. These goals often conflict.
type Color = "red" | "green" | "blue";
// We want both: check the values AND remember exactly which ones.Annotation Loses Literal Narrowing
Adding a type annotation validates the object but widens the values. The compiler forgets the exact literals you wrote.
type Config = { color: string; retries: number };
const cfg: Config = { color: "red", retries: 3 };
// cfg.color is string, not "red" anymore.
console.log(cfg.color.toUpperCase());Why Widening Hurts
If color is widened to string, you lose the ability to use it in places that need the specific literal, like indexing into another type.
const cfg = { color: "red" } as { color: string };
// const exact: "red" = cfg.color; // Error: string not assignable to "red"
console.log(cfg.color);No Annotation Loses Validation
Dropping the annotation keeps precise inference but removes the safety net. Typos and wrong shapes go unnoticed.
const cfg = { color: "rad", retries: 3 };
// No error on the typo "rad" because nothing validates it.
console.log(cfg.color);The Validation Gap
Without a check, an invalid value like a misspelled color silently flows through your program and may break far from where the mistake was made.
const palette = { primary: "blu" }; // typo, no error
console.log(palette.primary); // "blu"Enter the satisfies Operator
The satisfies operator validates an expression against a type while preserving the precise inferred type. Best of both worlds.
type Config = { color: string; retries: number };
const cfg = { color: "red", retries: 3 } satisfies Config;
console.log(cfg.color); // type is "red", value validatedsatisfies Keeps Literals
After satisfies, cfg.color is still the literal "red", not the wide string. Inference is preserved.
type Config = { color: string };
const cfg = { color: "red" } satisfies Config;
const exact: "red" = cfg.color; // OK
console.log(exact);satisfies Still Validates
If the object does not match the type, satisfies reports an error, just like an annotation would.
type Config = { retries: number };
// const bad = { retries: "three" } satisfies Config; // Error
const good = { retries: 3 } satisfies Config;
console.log(good.retries);The Motivating Example
Consider a route map where keys must be known routes and values are handler names. We want autocomplete on keys AND validated values, which satisfies delivers.
type Routes = Record<string, { method: "GET" | "POST" }>;
const routes = {
home: { method: "GET" },
submit: { method: "POST" }
} satisfies Routes;
console.log(routes.home.method); // "GET" literalComparing the Three Approaches
Annotation: validates, widens. No annotation: precise, unvalidated. satisfies: validates and precise. The third wins for configs.
type C = { color: string };
const a: C = { color: "red" }; // widened
const b = { color: "red" }; // unvalidated
const c = { color: "red" } satisfies C; // validated + precise
console.log(a.color, b.color, c.color);When to Reach for satisfies
Use satisfies whenever you define a constant value that must conform to a type but you also want to use its exact contents later.
type Sizes = Record<string, number>;
const sizes = { sm: 8, md: 16, lg: 24 } satisfies Sizes;
// keys "sm" | "md" | "lg" stay known for autocomplete
console.log(sizes.md);Quick Check: Why satisfies
Test your understanding of why satisfies exists.
Recap: Why satisfies Exists
You learned that annotations validate but widen, no annotation preserves but does not validate, and satisfies gives validation plus precise inference, the ideal for configuration objects.
type C = { color: string };
const c = { color: "blue" } satisfies C;
const exact: "blue" = c.color;
console.log(exact);Frequently asked questions
Is the “Why satisfies Exists” lesson free?
Yes — the full text of “Why satisfies Exists” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.
What will I learn in “Why satisfies Exists”?
The gap between annotation and inference that satisfies fills. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start TypeScript Academy?
No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 1 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Why satisfies Exists” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this TypeScript Academy lesson?
Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.