Typorithm · hard

9. satisfies, const assertions and configuration

typescriptstable-typescriptinterviewindependent-practice
Saved on this device
Full lesson · 4 learning steps Open any exercise for focused practice
1

Step 1 of 4 · intro card

9. satisfies, const assertions and configuration

0%

Concept and contract

Check a configuration contract while retaining useful inferred information.

An annotation, an assertion and satisfies answer different questions. An annotation chooses the variable’s public type. An assertion asks the compiler to trust a compatible interpretation. satisfies checks assignability against a contract while generally retaining the expression’s useful inferred shape.

Use satisfies for a configuration object whose keys and values need checking without losing specific members. Contextual typing still participates, so it is too strong to claim that satisfies never affects inference. Inspect the resulting property types rather than memorizing an absolute slogan.

Mechanism and worked example

The example checks every route against a path contract.

The example checks every route against a path contract. The paths remain literal strings because as const preserves them, while satisfies verifies the required route vocabulary and slash-prefixed shape. The actual object still supplies all runtime values.

A const assertion makes properties readonly to checked consumers and preserves literals; it does not freeze the object. The combined pattern is useful for small registries, but mutable application state usually needs an explicit wider state type rather than permanently literal properties.

Complete worked example

typescript

type Routes = Record<"home" | "roles", `/${string}`>;
const routes = { home: "/", roles: "/vacancies" } as const satisfies Routes;
const rolesPath: "/vacancies" = routes.roles;
Reasoning
as const preserves literals; satisfies independently checks the registry contract.

Counterexamples and common mistakes

The rejected route omits the required slash.

The rejected route omits the required slash. A broad as Routes assertion could conceal a malformed value instead of checking it. A satisfies expression should fail construction when a configuration value does not meet the intended contract.

Do not use the checked registry as proof that a runtime URL is safe to navigate. A route path contract is not an origin allowlist, authorization rule or URL parser. External destinations need separate parsing and security policy.

Rejected counterexample: expected compiler diagnostic

typescript

type Routes = Record<"home", `/${string}`>;
// @ts-expect-error The path must begin with a slash.
const invalid = { home: "home" } satisfies Routes;
Reasoning
A malformed path is rejected rather than reinterpreted by an assertion.

Independent practice

Create a two-country configuration mapping US and UA to currency and display name.

Create a two-country configuration mapping US and UA to currency and display name. Preserve literal currency values while requiring both entries and rejecting unsupported country keys. First write a concrete object, then add the checking expression.

Read one currency into a variable requiring its exact literal. Reject a missing country and a misspelled key in separate compiler tests. Optional extension: derive the Country union from a canonical key tuple instead of maintaining two competing vocabularies.

Step 1: Construct both market entries. Step 2: Check the complete vocabulary while retaining literals.

Practice starter: solve before reading the solution

typescript

type Country = "US" | "UA";
type Market = { currency: string; name: string };
// Add a complete checked configuration while preserving currency literals.
Reasoning
Build the concrete two-entry registry before adding the derived contract.
  • Hint 1: as const preserves literal values.
  • Hint 2: satisfies checks the expected registry shape.

Solution, tests and trade-offs

The solution combines a finite Record with as const satisfies.

The solution combines a finite Record with as const satisfies. It keeps the useful literal USD and UAH values without claiming the runtime object was frozen. A missing entry remains a construction error because the expected key set is complete.

Compile the exact-literal receiving assignment and check the runtime displayed names. Remove an entry temporarily to see the completeness diagnostic. These small checks develop configuration reasoning without turning the exercise into an advanced inference puzzle.

Practice solution and acceptance checks

typescript

type Country = "US" | "UA";
type Market = { currency: string; name: string };
const markets = { US: {currency:"USD",name:"United States"}, UA:{currency:"UAH",name:"Ukraine"} } as const satisfies Record<Country, Market>;
const currency: "UAH" = markets.UA.currency;

// Acceptance checks
if(currency!=="UAH"||markets.US.name!=="United States")throw new Error("markets");
Reasoning
Both countries are required while their exact currency values remain available.

Interview reasoning and next step

In interviews, compare : Contract, as Contract and satisfies Contract using the same object.

In interviews, compare : Contract, as Contract and satisfies Contract using the same object. Explain the role of contextual typing and why an assertion is not a validation tool. Include runtime immutability as a separate concern.

Next, study const type parameters, which preserve literal information through generic calls. They can reduce repeated caller-side const assertions, but they also require compatible readonly constraints and careful public API design.

Optional stretch — after the core task works: Derive the country union from one canonical key tuple.

Check a configuration contract while retaining useful inferred information.