0Pricing
TypeScript Academy · Lesson

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 validated

satisfies 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" literal

Comparing 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.

All lessons in this course

  1. Why satisfies Exists
  2. satisfies vs Type Annotation
  3. satisfies vs as Assertion
  4. Practical satisfies Patterns
← Back to TypeScript Academy