Lesson 19 of 80
A compile-time-only promise that a property won't be reassigned — real protection against a whole class of bugs, but, like every TypeScript check, it evaporates the moment the code actually runs.
11 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.
Question 1 of 2
Does marking a function parameter's type as readonly prevent a plain JavaScript caller from mutating the object at runtime?
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
interface Point { readonly x: number; readonly y: number }
function move(p: Point) {
p.x = p.x + 1; // ❌ compile error — x is readonly
}
const origin: Point = { x: 0, y: 0 };
readonly on a property means the compiler rejects any code that tries to reassign it after creation. This is exactly the JavaScript track's immutability discipline, enforced by the type checker instead of by convention — the same reasoning that makes [...state, item] safer than state.push(item) for React state, now caught at compile time instead of only in code review.
readonly is shallow, exactly like Object.freezeinterface Config { readonly nested: { value: number } }
const c: Config = { nested: { value: 1 } };
c.nested = { value: 2 }; // ❌ blocked
c.nested.value = 2; // ✅ allowed — readonly didn't reach inside
Just like Object.freeze from the JavaScript track only freezes one level deep, readonly only protects the property it's directly applied to — nested objects need their own readonly (or Readonly<T> applied recursively) to actually be protected all the way down.
interface Point { readonly x: number; readonly y: number } — x and y can be read, never reassigned after creation.
readonly is erased at compile time, exactly like every other type-only feature — nothing stops a plain JavaScript caller (or a type assertion) from mutating the object at runtime. It's a discipline the compiler enforces on your TypeScript code, not a runtime guarantee.readonly array can still be reassigned to a different array unless the variable holding it is also const — readonly protects the array's contents, const protects the binding; they're independent and often both needed together.readonly is the type system giving you a compile-time nudge toward the "produce a new value instead of mutating" habit that React's reconciliation depends on."Why would you mark a function parameter as readonly?" checks whether you connect it to the real bug class it prevents (accidental mutation of shared data) rather than treating it as decorative syntax.