0
0
Typescriptprogramming~15 mins

Readonly class properties in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Readonly class properties
What is it?
Readonly class properties are special properties in TypeScript classes that cannot be changed after they are first set. They help keep data safe by preventing accidental changes. You can set them once when creating an object, but after that, their value stays fixed. This makes your code more predictable and easier to understand.
Why it matters
Without readonly properties, important data inside objects can be changed by mistake, causing bugs that are hard to find. Readonly properties protect these values, making programs safer and more reliable. This is especially important in big projects where many parts of the code work together and accidental changes can cause big problems.
Where it fits
Before learning readonly properties, you should understand basic TypeScript classes and how to create properties. After this, you can learn about immutability, readonly arrays, and advanced type safety features in TypeScript.
Mental Model
Core Idea
A readonly class property is like a label on a box that you can write once but never erase or change.
Think of it like...
Imagine you write your name on a notebook cover with a permanent marker. You can’t erase or change it later, so everyone knows who owns it. Readonly properties work the same way in code—they keep certain values fixed forever.
Class Example
┌─────────────────────────────┐
│ class Person {              │
│   readonly name: string;   │
│                             │
│   constructor(name: string) │
│   {                         │
│     this.name = name;       │
│   }                         │
│ }                           │

Usage
person = new Person('Anna')
person.name = 'Bob'  // Error: Cannot assign to 'name' because it is a read-only property.
Build-Up - 7 Steps
1
FoundationBasic class properties in TypeScript
🤔
Concept: Learn how to create and use simple properties inside a TypeScript class.
class Car { model: string; constructor(model: string) { this.model = model; } } const car = new Car('Toyota'); console.log(car.model); // Output: Toyota car.model = 'Honda'; console.log(car.model); // Output: Honda
Result
You can create a class with properties that can be read and changed anytime.
Understanding how normal class properties work is essential before adding restrictions like readonly.
2
FoundationIntroducing readonly keyword
🤔
Concept: Learn how to make a class property readonly so it cannot be changed after initialization.
class Car { readonly model: string; constructor(model: string) { this.model = model; } } const car = new Car('Toyota'); console.log(car.model); // Output: Toyota car.model = 'Honda'; // Error: Cannot assign to 'model' because it is a read-only property.
Result
The property 'model' can be set once in the constructor but cannot be changed later.
Knowing that readonly properties protect data from accidental changes helps write safer code.
3
IntermediateReadonly properties with default values
🤔
Concept: You can assign a readonly property a default value directly in the class without using the constructor.
class Book { readonly title: string = 'Unknown Title'; } const book = new Book(); console.log(book.title); // Output: Unknown Title book.title = 'New Title'; // Error: Cannot assign to 'title' because it is a read-only property.
Result
Readonly properties can have default values that never change after creation.
This shows flexibility in how readonly properties can be initialized, either in constructor or directly.
4
IntermediateReadonly with complex types and objects
🤔
Concept: Readonly applies only to the property reference, not to the internal state of objects it points to.
class User { readonly preferences: { theme: string }; constructor() { this.preferences = { theme: 'light' }; } } const user = new User(); user.preferences.theme = 'dark'; // Allowed user.preferences = { theme: 'dark' }; // Error: Cannot assign to 'preferences' because it is a read-only property.
Result
You cannot change the preferences object itself, but you can change its contents.
Understanding this prevents confusion about what readonly protects—only the reference, not the object's internals.
5
IntermediateReadonly properties with parameter properties
🤔
Concept: You can declare readonly properties directly in the constructor parameters for concise code.
class Point { constructor(readonly x: number, readonly y: number) {} } const p = new Point(5, 10); console.log(p.x, p.y); // Output: 5 10 p.x = 20; // Error: Cannot assign to 'x' because it is a read-only property.
Result
Readonly properties can be declared and initialized in one step inside the constructor.
This syntax saves code and clearly shows which properties are readonly from the start.
6
AdvancedReadonly properties and inheritance
🤔Before reading on: Can a subclass change a readonly property declared in its parent class? Commit to yes or no.
Concept: Explore how readonly properties behave when a class inherits from another class.
class Animal { readonly species: string; constructor(species: string) { this.species = species; } } class Dog extends Animal { constructor() { super('Canine'); // this.species = 'Feline'; // Error: Cannot assign to 'species' because it is a read-only property. } } const dog = new Dog(); console.log(dog.species); // Output: Canine
Result
Readonly properties set in the parent class cannot be changed by subclasses after construction.
Knowing this prevents accidental overrides and preserves data integrity across class hierarchies.
7
ExpertReadonly properties and TypeScript's structural typing
🤔Before reading on: Does readonly affect how TypeScript compares object types structurally? Commit to yes or no.
Concept: Understand how readonly properties influence TypeScript's type compatibility and assignability rules.
interface ReadonlyPoint { readonly x: number; readonly y: number; } interface MutablePoint { x: number; y: number; } const mutable: MutablePoint = { x: 1, y: 2 }; const readonlyPoint: ReadonlyPoint = mutable; // Allowed const mutable2: MutablePoint = readonlyPoint; // Error: Cannot assign to 'mutable2' because it is a read-only type.
Result
Readonly properties make types stricter and prevent assigning readonly objects to mutable types.
Understanding this helps avoid subtle bugs when mixing readonly and mutable types in complex codebases.
Under the Hood
Readonly properties in TypeScript are enforced at compile time by the TypeScript compiler. They prevent assignment to these properties after initialization. At runtime, JavaScript does not enforce readonly, so the protection is only during development. TypeScript uses type checking rules to track which properties are readonly and disallow writes to them after construction.
Why designed this way?
TypeScript was designed to add safety without changing JavaScript runtime behavior. Readonly properties provide a way to catch errors early without runtime overhead. This design balances safety and performance, allowing gradual adoption and compatibility with existing JavaScript code.
┌───────────────────────────────┐
│ TypeScript Compiler            │
│                               │
│  ┌───────────────┐            │
│  │ Source Code   │            │
│  └──────┬────────┘            │
│         │                     │
│         ▼                     │
│  ┌───────────────┐            │
│  │ Type Checker  │            │
│  │ - Detects     │            │
│  │   readonly    │            │
│  │   violations  │            │
│  └──────┬────────┘            │
│         │                     │
│         ▼                     │
│  ┌───────────────┐            │
│  │ JavaScript    │            │
│  │ Output        │            │
│  │ (no readonly) │            │
│  └───────────────┘            │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does readonly prevent changing the contents of an object property? Commit to yes or no.
Common Belief:Readonly means the entire object and its contents cannot be changed.
Tap to reveal reality
Reality:Readonly only prevents reassigning the property itself, but the contents of an object property can still be changed.
Why it matters:Assuming readonly protects nested data can lead to bugs where internal state changes unexpectedly.
Quick: Can readonly properties be changed after object creation using JavaScript tricks? Commit to yes or no.
Common Belief:Readonly properties are enforced at runtime and cannot be changed by any means.
Tap to reveal reality
Reality:Readonly is only enforced by TypeScript at compile time; at runtime, JavaScript allows changes unless frozen manually.
Why it matters:Relying on readonly for runtime immutability can cause security or stability issues if JavaScript code modifies properties.
Quick: Does declaring a property readonly mean it must be initialized immediately? Commit to yes or no.
Common Belief:Readonly properties must always be initialized where they are declared.
Tap to reveal reality
Reality:Readonly properties can be initialized either where declared or inside the constructor, but must be assigned exactly once.
Why it matters:Misunderstanding initialization rules can cause confusion and errors when designing classes.
Quick: Can a subclass override a readonly property from its parent class? Commit to yes or no.
Common Belief:Subclasses can freely change readonly properties inherited from parent classes.
Tap to reveal reality
Reality:Readonly properties cannot be reassigned by subclasses after the parent constructor sets them.
Why it matters:Incorrect assumptions here can break class invariants and cause unexpected behavior.
Expert Zone
1
Readonly only protects assignment to the property itself, not the mutability of objects it references, so deep immutability requires additional patterns.
2
Readonly properties can improve performance by enabling better compiler optimizations and clearer intent, but they do not add runtime overhead.
3
Using readonly with parameter properties in constructors reduces boilerplate and clarifies intent, but can obscure where properties are set if overused.
When NOT to use
Avoid readonly properties when you need to update the property value after object creation. Instead, use normal mutable properties or methods to change state. For deep immutability, consider using libraries like Immutable.js or readonly utility types combined with frozen objects.
Production Patterns
Readonly properties are commonly used for configuration objects, constants, and data transfer objects (DTOs) to prevent accidental changes. They are also used in APIs to enforce immutability contracts and improve code maintainability.
Connections
Immutable data structures
Builds-on
Readonly properties are a simple form of immutability, helping understand how to protect data from changes in larger immutable data patterns.
Functional programming
Related pattern
Functional programming emphasizes immutability; readonly properties in classes help bring some of these benefits into object-oriented code.
Legal contracts
Analogy in a different field
Just like a legal contract sets fixed terms that cannot be changed after signing, readonly properties set fixed values that cannot be altered, ensuring trust and predictability.
Common Pitfalls
#1Trying to change a readonly property after object creation.
Wrong approach:class User { readonly id: number; constructor(id: number) { this.id = id; } } const user = new User(1); user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.
Correct approach:class User { readonly id: number; constructor(id: number) { this.id = id; } } const user = new User(1); // Do not assign user.id again; create a new User if needed.
Root cause:Misunderstanding that readonly means the property can never be assigned again after construction.
#2Assuming readonly makes nested objects immutable.
Wrong approach:class Config { readonly settings = { theme: 'light' }; } const config = new Config(); config.settings.theme = 'dark'; // Allowed, but unexpected
Correct approach:class Config { readonly settings = Object.freeze({ theme: 'light' }); } const config = new Config(); config.settings.theme = 'dark'; // Error at runtime due to freeze
Root cause:Confusing readonly property reference protection with deep immutability of object contents.
#3Not initializing readonly properties properly.
Wrong approach:class Product { readonly name: string; } const p = new Product(); console.log(p.name); // Error: Property 'name' has no initializer and is not assigned in constructor
Correct approach:class Product { readonly name: string; constructor(name: string) { this.name = name; } } const p = new Product('Book'); console.log(p.name); // Output: Book
Root cause:Forgetting that readonly properties must be assigned exactly once, either at declaration or in constructor.
Key Takeaways
Readonly class properties in TypeScript prevent reassignment after initialization, making your code safer and more predictable.
Readonly only protects the property reference, not the internal state of objects it points to, so deep immutability requires extra steps.
You can initialize readonly properties either directly or inside the constructor, but they must be assigned exactly once.
Readonly properties help enforce contracts in class hierarchies and improve code maintainability by preventing accidental changes.
Understanding readonly's compile-time enforcement and runtime behavior prevents common bugs and misuse in TypeScript projects.