Lesson 36 of 80
Structural typing says extra properties are fine — so why does passing an object literal with one extra field fail, while assigning that exact same shape through a variable first doesn't?
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
Structural typing, covered elsewhere in this track, says a value is assignable to a type as long as it has at least the required shape — extra properties are normally completely fine. That's what makes structural typing useful: you can pass a bigger, more detailed object anywhere a smaller shape is expected.
Except when you pass a fresh object literal directly. Then TypeScript runs a stricter check called an excess property check: any property on that literal that isn't part of the target type is flagged as an error, even though the general structural-typing rule would otherwise allow it. The check exists specifically to catch typos and mistakes — a fresh literal has no other purpose than being that one argument, so an unrecognized property on it is almost certainly a mistake, not a deliberate extra field.
interface Config {
mode: "dark" | "light";
}
function apply(cfg: Config) { /* ... */ }
apply({ mode: "dark", retries: 3 });
// ❌ Object literal may only specify known properties, and
// 'retries' does not exist in type 'Config'
const draft = { mode: "dark" as const, retries: 3 };
apply(draft);
// ✅ no error — draft is structurally compatible (it has `mode`);
// the extra `retries` is allowed once it's not a fresh literal
Both calls pass an object with the exact same shape. The literal, passed directly, gets the tighter freshness check and is rejected. The identical shape, stored in draft first, is checked under the ordinary structural-typing rule and passes — draft has everything Config requires, and extra properties on a non-literal value are simply not a problem.
apply({ ...draft, mode: "dark" }) is not treated as "fresh" the same way a plain literal is, so it bypasses excess property checking even though it's still written inline at the call site.satisfies isn't a workaround or a hack. It's often the genuinely correct choice when you deliberately want a value checked structurally rather than by freshness — the two checks exist for different purposes, and picking the right one is a real design decision, not a way to silence an error.This is a concrete, specific carve-out from the structural typing covered elsewhere in this track — being able to explain why the same shape is checked two different ways (rather than just knowing "assign it to a variable first to fix the error") is what separates having memorized a workaround from actually understanding what TypeScript is protecting against.