0
0
Typescriptprogramming~15 mins

Readonly properties in interfaces in Typescript - Deep Dive

Choose your learning style9 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.