Bird
Raised Fist0
Typescriptprogramming~15 mins

Readonly properties in interfaces in Typescript - Deep Dive

Choose your learning style10 modes available
Overview - Readonly properties in interfaces
What is it?
Readonly properties in interfaces are a way to define object properties that cannot be changed after the object is created. They tell TypeScript that once a value is set, it should not be modified. This helps catch mistakes where code tries to change something that should stay constant. It is like putting a 'Do Not Edit' sign on certain parts of an object.
Why it matters
Without readonly properties, bugs can happen when parts of a program accidentally change values that should stay fixed. This can cause unexpected behavior and make programs harder to understand and maintain. Readonly properties help keep data safe and predictable, making code more reliable and easier to debug.
Where it fits
Before learning readonly properties, you should understand basic TypeScript interfaces and how to define object shapes. After this, you can learn about readonly arrays, readonly tuples, and advanced immutability patterns in TypeScript.
Mental Model
Core Idea
Readonly properties are like locked boxes in an object that you can look at but cannot change once set.
Think of it like...
Imagine a library book with a special sticker that says 'Do Not Write Inside.' You can read the book, but you cannot write notes or change anything inside it. Readonly properties work the same way for object properties.
Interface Example:
┌─────────────────────────────┐
│ interface Person {          │
│   readonly name: string;    │
│   age: number;              │
│ }                          │
└─────────────────────────────┘

Object Example:
{
  name: "Alice"  ← locked, cannot change
  age: 30         ← can change
}
Build-Up - 7 Steps
1
FoundationUnderstanding TypeScript Interfaces
🤔
Concept: Learn what interfaces are and how they describe object shapes.
In TypeScript, an interface defines the structure of an object by listing its properties and their types. For example: interface Car { make: string; year: number; } This means any object of type Car must have a 'make' string and a 'year' number.
Result
You can create objects that match the interface, and TypeScript will check their shape.
Knowing interfaces lets you describe what objects should look like, which is the base for adding readonly properties.
2
FoundationBasic Property Declaration in Interfaces
🤔
Concept: Learn how to declare properties inside interfaces without restrictions.
Properties in interfaces are by default writable. For example: interface Book { title: string; pages: number; } let myBook: Book = { title: "Story", pages: 100 }; myBook.pages = 120; // This is allowed because 'pages' is writable.
Result
You can change property values freely unless specified otherwise.
Understanding default writable properties helps appreciate why readonly is needed.
3
IntermediateIntroducing Readonly Modifier
🤔Before reading on: do you think adding 'readonly' means the property can never be set, or it can be set once and then not changed? Commit to your answer.
Concept: The 'readonly' keyword marks properties that can be set once but not changed later.
You add 'readonly' before a property name in an interface to make it immutable after initialization: interface User { readonly id: number; name: string; } let user: User = { id: 1, name: "Bob" }; user.name = "Robert"; // allowed user.id = 2; // error: cannot assign to 'id' because it is a readonly property
Result
TypeScript will give an error if you try to change a readonly property after the object is created.
Knowing that readonly properties can be set once but not changed prevents accidental mutations.
4
IntermediateReadonly Properties and Object Mutability
🤔If an object property is readonly, can you change the contents of that property if it is an object or array? Commit to your answer.
Concept: Readonly only prevents reassigning the property itself, not the contents if the property is an object or array.
For example: interface Data { readonly items: string[]; } let data: Data = { items: ["a", "b"] }; data.items.push("c"); // allowed // data.items = ["x"] // error: cannot reassign This means the array reference is locked, but the array contents can change.
Result
Readonly protects the reference, not the internal state of objects or arrays.
Understanding this helps avoid false assumptions about immutability and guides when to use deeper immutability techniques.
5
IntermediateReadonly with Optional Properties
🤔
Concept: Readonly can be combined with optional properties to control mutability and presence.
You can declare a property as both readonly and optional: interface Config { readonly timeout?: number; } let config1: Config = {}; let config2: Config = { timeout: 1000 }; // config2.timeout = 2000; // error: readonly property Optional means the property may be missing, readonly means if present, it cannot change.
Result
You get flexible but safe object shapes that prevent unwanted changes.
Knowing how to combine readonly and optional properties allows precise control over object design.
6
AdvancedReadonly in Nested Interfaces
🤔If a nested object property is readonly, can you change its inner properties? Commit to your answer.
Concept: Readonly applies only to the immediate property, not deeply nested properties unless specified recursively.
Example: interface Address { street: string; city: string; } interface Person { readonly address: Address; } let p: Person = { address: { street: "Main", city: "Town" } }; p.address.city = "New Town"; // allowed // p.address = { street: "2nd", city: "City" }; // error To make nested properties readonly, use utility types like Readonly.
Result
Readonly protects the top-level reference but not nested objects unless explicitly made readonly.
Knowing this prevents bugs where nested data changes unexpectedly despite readonly on the parent.
7
ExpertReadonly Utility Type and Deep Immutability
🤔Does TypeScript's built-in Readonly make all nested properties readonly by default? Commit to your answer.
Concept: Readonly makes all top-level properties readonly but does not deeply freeze nested objects by default.
TypeScript provides a utility type: interface Person { name: string; address: { street: string; }; } const readonlyPerson: Readonly = { name: "Alice", address: { street: "Main" } }; readonlyPerson.name = "Bob"; // error readonlyPerson.address.street = "2nd"; // allowed To deeply freeze, you need custom types or libraries that recursively apply readonly.
Result
Readonly helps prevent top-level mutations but deeper immutability requires extra work.
Understanding the limits of Readonly guides better design for truly immutable data structures.
Under the Hood
At compile time, TypeScript uses readonly modifiers to check assignments and prevent code that tries to change readonly properties. This does not affect the generated JavaScript, which has no readonly enforcement at runtime. The readonly keyword is a compile-time safety feature that helps developers avoid bugs by catching illegal mutations early.
Why designed this way?
TypeScript was designed to add safety without changing JavaScript runtime behavior. Readonly properties provide immutability guarantees during development without runtime overhead or breaking JavaScript compatibility. This design balances safety and flexibility.
TypeScript Compile-Time Check Flow:

┌───────────────┐
│ Source Code   │
│ with readonly │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ TypeScript    │
│ Compiler      │
│ Checks if    │
│ readonly is   │
│ violated     │
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ JavaScript    │
│ Output       │
│ (no readonly) │
└───────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does 'readonly' make the property immutable at runtime? Commit to yes or no.
Common Belief:Readonly properties prevent any changes to the property value at runtime.
Tap to reveal reality
Reality:Readonly only prevents changes at compile time; JavaScript does not enforce it at runtime.
Why it matters:Assuming runtime immutability can lead to bugs if code changes readonly properties via JavaScript directly or bypasses TypeScript checks.
Quick: If a property is readonly and is an object, can you change its inner fields? Commit to yes or no.
Common Belief:Readonly means the entire object and all nested properties cannot be changed.
Tap to reveal reality
Reality:Readonly only locks the reference; nested properties can still be modified unless they are also readonly.
Why it matters:Misunderstanding this can cause unexpected mutations and bugs in complex objects.
Quick: Does the Readonly utility type make nested properties readonly by default? Commit to yes or no.
Common Belief:Readonly makes all properties, including nested ones, readonly automatically.
Tap to reveal reality
Reality:Readonly only makes the first level properties readonly; nested objects remain mutable.
Why it matters:Relying on Readonly for deep immutability can cause subtle bugs when nested data changes unexpectedly.
Quick: Can you assign a new value to a readonly property during object creation? Commit to yes or no.
Common Belief:Readonly properties cannot be assigned any value, even when creating the object.
Tap to reveal reality
Reality:Readonly properties can be assigned once during object creation or initialization.
Why it matters:Thinking readonly means 'never assign' would prevent proper object initialization and confuse developers.
Expert Zone
1
Readonly properties only affect compile-time checks and do not add runtime immutability, so runtime code can still mutate objects if not careful.
2
Combining readonly with utility types like ReadonlyArray or custom deep readonly types is necessary for full immutability in complex data structures.
3
Readonly properties can improve performance by enabling better compiler optimizations and clearer intent, but overusing them can reduce flexibility.
When NOT to use
Avoid readonly properties when you need mutable state or expect properties to change frequently. Instead, use normal writable properties or state management patterns. For deep immutability, consider libraries like Immer or Immutable.js that enforce runtime immutability.
Production Patterns
In production, readonly properties are used to define configuration objects, constants, and API response types to prevent accidental changes. They are combined with readonly arrays and mapped types to create safe, immutable data models that reduce bugs and improve maintainability.
Connections
Immutable Data Structures
Readonly properties are a shallow form of immutability, while immutable data structures enforce deep immutability.
Understanding readonly properties helps grasp the basics of immutability, which is key in functional programming and state management.
Const Keyword in JavaScript
Both readonly properties and const prevent reassignment, but const applies to variables, readonly applies to object properties.
Knowing the difference clarifies how to control mutability at different levels in code.
Access Control in Object-Oriented Programming
Readonly properties relate to access control by restricting write access, similar to private or protected modifiers but focused on immutability.
This connection shows how readonly fits into broader concepts of controlling how data is accessed and modified.
Common Pitfalls
#1Trying to change a readonly property after object creation.
Wrong approach:interface User { readonly id: number; } let u: User = { id: 1 }; u.id = 2; // Error: Cannot assign to 'id' because it is a readonly property.
Correct approach:interface User { readonly id: number; } let u: User = { id: 1 }; // Do not reassign u.id after creation.
Root cause:Misunderstanding that readonly means 'set once' not 'never set'.
#2Assuming readonly makes nested objects immutable.
Wrong approach:interface Data { readonly info: { value: number } } let d: Data = { info: { value: 10 } }; d.info.value = 20; // Allowed, but unexpected mutation.
Correct approach:Use deep readonly types or freeze nested objects explicitly to prevent mutation.
Root cause:Confusing shallow readonly with deep immutability.
#3Expecting runtime errors when modifying readonly properties.
Wrong approach:interface Config { readonly setting: string; } const c: Config = { setting: "on" }; c.setting = "off"; // No runtime error, only compile-time error.
Correct approach:Use runtime checks or libraries if runtime immutability is required.
Root cause:Believing TypeScript readonly enforces runtime immutability.
Key Takeaways
Readonly properties in interfaces prevent reassignment of properties after object creation, helping catch bugs early.
Readonly only applies at compile time and does not enforce immutability at runtime in JavaScript.
Readonly locks the reference of a property but does not make nested objects or arrays immutable by default.
Combining readonly with utility types and deep immutability patterns is necessary for fully immutable data structures.
Understanding readonly properties improves code safety, clarity, and maintainability by signaling which data should not change.

Practice

(1/5)
1.

What does the readonly keyword do when used in a TypeScript interface property?

easy
A. Prevents the property from being changed after initialization
B. Allows the property to be changed anytime
C. Makes the property optional
D. Makes the property private

Solution

  1. Step 1: Understand the purpose of readonly in interfaces

    The readonly keyword marks a property so it cannot be reassigned after the object is created.
  2. Step 2: Compare with other options

    Options A, B, and D describe different behaviors: A makes optional, B allows changes, D changes visibility, none of which match readonly.
  3. Final Answer:

    Prevents the property from being changed after initialization -> Option A
  4. Quick Check:

    readonly means no changes allowed after creation [OK]
Hint: Readonly means no changes allowed after creation [OK]
Common Mistakes:
  • Thinking readonly makes property optional
  • Confusing readonly with private or protected
  • Assuming readonly allows reassignment
2.

Which of the following is the correct syntax to declare a readonly property id of type number in a TypeScript interface?

interface User {
?
}
easy
A. id: readonly number;
B. id readonly: number;
C. readonly: id number;
D. readonly id: number;

Solution

  1. Step 1: Recall correct readonly syntax in interfaces

    The correct syntax places readonly before the property name, then a colon and the type.
  2. Step 2: Check each option

    readonly id: number; matches the correct syntax. Options A, B, and C have incorrect order or misplaced keywords.
  3. Final Answer:

    readonly id: number; -> Option D
  4. Quick Check:

    Readonly keyword comes before property name [OK]
Hint: Put readonly before property name in interface [OK]
Common Mistakes:
  • Placing readonly after property name
  • Using readonly as a type
  • Misplacing colons or semicolons
3.

Consider the following code:

interface Point {
readonly x: number;
readonly y: number;
}

const p: Point = { x: 10, y: 20 };
p.x = 30;
console.log(p.x);

What will happen when this code runs?

medium
A. It will print 30
B. It will print 10
C. Compile-time error due to assignment to readonly property
D. Runtime error when assigning to readonly property

Solution

  1. Step 1: Understand readonly behavior at compile time

    TypeScript prevents assigning a new value to a readonly property during compilation, causing an error before running.
  2. Step 2: Check runtime behavior

    Since TypeScript errors stop compilation, the code won't run, so no runtime error or output occurs.
  3. Final Answer:

    Compile-time error due to assignment to readonly property -> Option C
  4. Quick Check:

    Readonly blocks assignment at compile time [OK]
Hint: Readonly errors happen at compile time, not runtime [OK]
Common Mistakes:
  • Thinking readonly allows reassignment at runtime
  • Expecting runtime error instead of compile error
  • Assuming original value changes silently
4.

Find the error in this code snippet:

interface Config {
readonly url: string;
}

const config: Config = { url: "https://api.com" };
config.url = "https://newapi.com";
medium
A. Cannot assign to 'url' because it is a readonly property
B. Missing semicolon after interface declaration
C. Type mismatch: string assigned to readonly property
D. Interface properties cannot be initialized

Solution

  1. Step 1: Identify the readonly property usage

    The property url is marked readonly, so it cannot be reassigned after initialization.
  2. Step 2: Check the assignment line

    The line config.url = "https://newapi.com"; tries to change a readonly property, causing a compile-time error.
  3. Final Answer:

    Cannot assign to 'url' because it is a readonly property -> Option A
  4. Quick Check:

    Readonly properties cannot be reassigned [OK]
Hint: Readonly properties cause errors if reassigned [OK]
Common Mistakes:
  • Ignoring readonly and trying to reassign
  • Confusing readonly with optional properties
  • Thinking semicolon or type is the error
5.

You want to create an interface Person with a readonly id and a mutable name. Which code correctly enforces this?

?
hard
A. interface Person { id: readonly number; name: string; }
B. interface Person { readonly id: number; name: string; }
C. interface Person { readonly id: number; readonly name: string; }
D. interface Person { id: number; name: readonly string; }

Solution

  1. Step 1: Define readonly and mutable properties correctly

    The readonly keyword must be before the property name to make it immutable; properties without it remain mutable.
  2. Step 2: Evaluate each option

    interface Person { readonly id: number; name: string; } correctly marks id as readonly and name as mutable. Options B, C, and D misuse readonly or make both properties readonly.
  3. Final Answer:

    interface Person { readonly id: number; name: string; } -> Option B
  4. Quick Check:

    Readonly before property name only for immutable fields [OK]
Hint: Put readonly before only the properties you want fixed [OK]
Common Mistakes:
  • Placing readonly after property name or type
  • Making all properties readonly by mistake
  • Confusing readonly with type annotations
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard