In TypeScript, a teammate silences an "Object literal may only specify known properties" error by writing `as Config` on the literal. What does that assertion give up, and what should they use instead — including where `satisfies Config` does and does not help?
answer
- assertion switches the check off
- silences the next typo too
- overlap guard is very weak
- satisfies checks without widening
- fix the type, not the expression
basics
~20 sas removes the literal's freshness, so it silences every excess-property complaint in that literal, not just the intended one — future typos included. The honest fixes change the type: declare the extra property, or widen the contract. satisfies checks without widening but still rejects undeclared keys.
solid answer
~50 sAn assertion tells the checker to stop reasoning, and it does so wholesale: `as Config` discards the literal's freshness, so every unknown key in that object becomes legal, including the one someone misspells next month. It keeps only a weak "do these types overlap sufficiently" guard, which a literal with several valid properties easily passes — so `{ color: "red", colour: "blue" } as Theme` compiles with the typo intact. If the extra key is legitimate, change the type: declare the property, or give the target an index signature and accept that unknown keys are now part of the contract. `satisfies Config` is a different tool and worth naming precisely: it verifies the literal against `Config` while keeping the literal's own narrower inferred type, and it still runs the excess-property check — so it is not an escape hatch for extra keys, it is how you keep inference without weakening the check.
code
typescript · 13 linestype Config = { host: string; port: number };
// satisfies: validated against Config, keeps the literal's own type
const good = { host: "localhost", port: 8080 } satisfies Config;
good.port.toFixed(0);
// @ts-expect-error satisfies still runs the excess property check
const typo = { host: "localhost", port: 8080, timeuot: 5 } satisfies Config;
// as: the check is switched off, the misspelled key survives
const asserted = { host: "localhost", port: 8080, timeuot: 5 } as Config;
console.log(good, typo, asserted);go deeper
Know that as is an assertion, not a conversion — it changes only what the compiler believes and emits nothing. Say that the usual right fix for the error is to correct the key or add it to the type.
Explain that the assertion strips the literal's freshness so the whole literal stops being checked, and that only a weak overlap rule remains. Describe what satisfies evaluates to and why it still rejects unknown keys.
Show the judgment: rank the fixes, name what an index signature costs, and give a concrete case where a typo survived an assertion. Be able to say where assertions are legitimate — validated boundary data — and how you keep them there.
Set the policy. Decide where assertions are permitted, how suppressions are reviewed, and which configuration surfaces get schema validation instead, so the codebase does not erode the small set of real bugs this check catches.
## What the assertion actually does `expr as T` is a compile-time instruction: treat this expression as `T`. It performs no conversion, emits nothing, and checks nothing at run time. Applied to an object literal it also strips the literal's *freshness*, which is the flag that enables excess-property checking. That is why the error disappears — the check was switched off, not satisfied. The only thing `as` still enforces is a comparability rule: the two types must overlap sufficiently in one direction or the other, otherwise you get error 2352 suggesting a conversion through `unknown`. That guard is far weaker than people assume: ```ts interface Theme { color: string; size: number } // compiles — 'colour' is a typo that survives the assertion const t = { color: "red", size: 2, colour: "blue" } as Theme; ``` Because `color` and `size` are present, the overlap test passes and the misspelling rides along. The assertion did not fix a modelling problem; it hid one, permanently, for that whole literal. ## The honest alternatives Work through them in order of preference: 1. **It really is a typo.** Fix the key. This is by far the most common case, and it is exactly what the error was for. 2. **The property is legitimate and belongs to the contract.** Declare it on the type — as a required or optional member. Every other unknown key keeps producing an error. 3. **The contract genuinely accepts arbitrary keys.** Give the target an index signature. Be honest about what this buys: you have declared that any key is valid, so no typo in that object will ever be reported again. Correct for a headers bag or a free-form metadata map; wrong for a settings object with a fixed vocabulary. 4. **The value is wider than the slot but that is fine.** Bind it to a variable first; widening drops freshness for the same reason `as` does, but at least it does not lie about the type. This is a mild code smell, not a fix. ## Where `satisfies` fits — and where it does not `satisfies` is often reached for as "the modern way to silence this", which misstates what it does. `expr satisfies T` checks that `expr` is assignable to `T` and then **evaluates to the expression's own inferred type**, not to `T`. The operand is still a fresh object literal, so excess-property checking still applies: ```ts type Config = { host: string; port: number }; // still an error: satisfies does not permit undeclared keys const c = { host: "localhost", port: 80, timeuot: 5 } satisfies Config; ``` What it does buy you is inference. An annotation widens the value to the declared type; `satisfies` validates against the declared type and keeps the narrower one: ```ts const routesAnnotated: Record<string, string> = { home: "/", about: "/about" }; routesAnnotated.typo; // allowed — key type is string const routes = { home: "/", about: "/about" } satisfies Record<string, string>; routes.home; // known key // routes.typo would be an error — the inferred key set is preserved ``` So the rule of thumb is: **`satisfies` for validation without widening; a declared type change for legitimately extra keys; `as` almost never.** ## When an assertion is defensible There are honest uses. When you know something the checker cannot — a value you have just validated at a boundary, a narrow re-typing of an intentionally loose external declaration — an assertion is the tool. The discipline is to make it small and local: assert the smallest expression, next to the reason, with a comment saying what makes it true. What you should not do is assert a whole configuration literal to make a red squiggle go away, because that particular assertion buys nothing and destroys the one automated typo check the object had. ## The review-level point Excess-property checking is one of the few places where TypeScript catches a real class of production bug — a stale or misspelled key that silently takes a default. It is already deliberately full of holes (an intermediate variable defeats it entirely). Every `as` on a literal removes what remains. That is worth a codebase-level position: assertions concentrated at boundaries, reviewed like any other suppression, and never used as a general remedy for a shape mismatch that the type could simply describe. ## How to answer it Say what the assertion switches off, show that it silences future typos and not just this one, name the three honest fixes, and correct the common misconception about `satisfies` — it is not a way to allow extra properties, it is a way to check without widening.
- When is a type assertion the right tool rather than a smell?When you know something the checker cannot and can say why — a value re-typed after a runtime validation at a boundary, or narrowing an intentionally loose external declaration. Keep it on the smallest expression, next to the justification. Asserting a whole config literal to clear an error is the case that buys nothing.
- Does `satisfies` change the emitted JavaScript?No. It is part of the type layer and is erased, exactly like an annotation — it produces no output and no runtime check. Its only effect is on what the compiler infers for the expression and on which errors you get.
- If you use `satisfies`, do you still need the annotation on the variable?Usually not, and adding both defeats the purpose: the annotation widens the variable to the declared type, throwing away the narrower type `satisfies` preserved. Use one or the other — annotate when you want the wide type, use `satisfies` when you want validation plus precise inference.
saying these in an interview costs you the question
- Calls `as` the standard fix for an excess-property error
- Thinks `satisfies` lets a literal carry undeclared properties
- Believes an assertion performs a runtime conversion or check
- Assumes the assertion only affects the one flagged property
- Reaches for an index signature without noting it disables typo checks