0
0
Typescriptprogramming~15 mins

Readonly properties in Typescript - Deep Dive

Choose your learning style9 modes available
Overview - Readonly properties
What is it?
Readonly properties in TypeScript are special object properties that cannot be changed after they are set. Once you assign a value to a readonly property, trying to change it later will cause an error. This helps keep data safe from accidental changes. It is like writing with a pen that cannot be erased.
Why it matters
Readonly properties exist to prevent bugs caused by unexpected changes to important data. Without them, programs might accidentally change values that should stay fixed, causing confusing errors. Using readonly properties makes code more reliable and easier to understand, especially in large projects where many parts share data.
Where it fits
Before learning readonly properties, you should understand basic TypeScript types and how to define interfaces or classes. After this, you can learn about immutability, readonly arrays, and advanced type features like mapped types that use readonly modifiers.
Mental Model
Core Idea
A readonly property is a value you can set once but never change again.
Think of it like...
It's like a name tag you write once at a party; once written, you can't change the name on it during the event.
Object with readonly properties:

┌───────────────┐
│   Person      │
│ ┌───────────┐ │
│ │ name: John│ │  <-- readonly
│ │ age: 30   │ │  <-- writable
│ └───────────┘ │
└───────────────┘

Trying to change 'name' causes error, but 'age' can change.
Build-Up - 7 Steps
1
FoundationUnderstanding basic properties
🤔
Concept: Learn what object properties are and how to assign values to them.
In TypeScript, objects have properties that hold values. For example: const person = { name: 'Alice', age: 25 }; Here, 'name' and 'age' are properties you can read and change freely: person.age = 26; // allowed person.name = 'Bob'; // allowed
Result
You can read and change properties anytime.
Knowing how normal properties work helps you see why restricting changes with readonly is useful.
2
FoundationIntroducing readonly keyword
🤔
Concept: Learn how to mark properties as readonly in interfaces or classes.
You can add 'readonly' before a property name to make it unchangeable: interface Person { readonly name: string; age: number; } const p: Person = { name: 'Alice', age: 25 }; p.age = 26; // allowed p.name = 'Bob'; // error: cannot assign to 'name' because it is a read-only property
Result
Trying to change a readonly property causes a compile-time error.
Readonly keyword enforces immutability at the property level, preventing accidental changes.
3
IntermediateReadonly in classes and constructors
🤔
Concept: How readonly properties work inside classes and how to set them during object creation.
In classes, readonly properties can be set only when declared or inside the constructor: class Person { readonly name: string; age: number; constructor(name: string, age: number) { this.name = name; // allowed here this.age = age; } } const p = new Person('Alice', 25); p.age = 26; // allowed p.name = 'Bob'; // error
Result
Readonly properties can be set once during construction but not changed later.
Readonly properties provide safe initialization points but protect data after creation.
4
IntermediateReadonly arrays and tuples
🤔Before reading on: do you think readonly arrays allow changing elements or not? Commit to your answer.
Concept: Learn how readonly applies to arrays and tuples to prevent modification of their contents.
TypeScript has 'readonly' versions of arrays and tuples: const arr: readonly number[] = [1, 2, 3]; arr[0] = 10; // error: cannot assign to '0' because it is a read-only property You cannot change elements or use methods that modify the array like push or pop. Readonly tuples work similarly: const tuple: readonly [number, string] = [1, 'a']; tuple[1] = 'b'; // error
Result
Readonly arrays and tuples prevent any changes to their elements.
Readonly arrays help keep collections stable and avoid bugs from unexpected changes.
5
IntermediateReadonly mapped types
🤔Before reading on: do you think you can make all properties readonly automatically? Commit to your answer.
Concept: Learn how to create new types with all properties marked readonly using mapped types.
TypeScript lets you create a readonly version of any type: type ReadonlyPerson = Readonly; This makes all properties readonly: const rp: ReadonlyPerson = { name: 'Alice', age: 25 }; rp.age = 26; // error You can also write your own mapped type: type MyReadonly = { readonly [P in keyof T]: T[P] }; This copies all properties but makes them readonly.
Result
Mapped types let you transform types to readonly versions easily.
Understanding mapped types unlocks powerful ways to control mutability across complex types.
6
AdvancedReadonly properties and structural typing
🤔Before reading on: do you think readonly properties affect type compatibility? Commit to your answer.
Concept: Explore how readonly properties influence TypeScript's structural type system and assignment compatibility.
TypeScript uses structural typing, meaning types are compatible if their shapes match. Readonly properties are part of the shape: interface A { readonly x: number } interface B { x: number } let a: A = { x: 1 }; let b: B = a; // allowed let c: A = b; // error: 'x' is not readonly in B Readonly properties make types stricter and prevent unsafe assignments.
Result
Readonly properties tighten type compatibility rules.
Knowing how readonly affects compatibility helps avoid subtle bugs in large codebases.
7
ExpertReadonly properties and performance
🤔Before reading on: do you think readonly properties improve runtime speed or just help at compile time? Commit to your answer.
Concept: Understand that readonly properties exist only at compile time and do not affect runtime performance directly.
Readonly is a TypeScript compile-time feature. It does not generate extra JavaScript code. At runtime, objects behave normally: const obj = { readonlyProp: 5 }; obj.readonlyProp = 10; // no error at runtime, but TypeScript warns during development Readonly helps developers catch errors early but does not optimize runtime speed or memory.
Result
Readonly properties provide safety during development but no runtime cost or benefit.
Understanding this prevents confusion about readonly's role and avoids expecting runtime optimizations.
Under the Hood
Readonly properties are enforced by the TypeScript compiler during static analysis. When you mark a property as readonly, the compiler tracks assignments to that property and raises errors if any code tries to modify it after initialization. However, this restriction exists only at compile time; the generated JavaScript code does not include any readonly enforcement. At runtime, the properties behave like normal properties unless you use JavaScript features like Object.freeze to enforce immutability.
Why designed this way?
TypeScript was designed to add safety without changing JavaScript's runtime behavior. Enforcing readonly only at compile time allows developers to catch mistakes early without impacting performance or compatibility. This design choice balances safety and flexibility, letting developers opt into immutability without forcing it at runtime, which could break existing JavaScript code.
TypeScript Compilation Flow:

┌───────────────┐
│ Source Code   │
│ (with readonly)│
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ TypeScript    │
│ Compiler      │
│ - Checks readonly
│ - Errors if violated
└──────┬────────┘
       │
       ▼
┌───────────────┐
│ JavaScript    │
│ Output Code   │
│ (no readonly) │
└───────────────┘

At runtime, readonly is not enforced.
Myth Busters - 4 Common Misconceptions
Quick: Does readonly prevent changing properties at runtime or only at compile time? Commit to your answer.
Common Belief:Readonly properties prevent any changes to the property value at runtime.
Tap to reveal reality
Reality:Readonly only prevents changes during TypeScript compilation; at runtime, properties can still be changed unless extra JavaScript measures are used.
Why it matters:Believing readonly enforces runtime immutability can lead to false confidence and bugs when JavaScript code modifies supposedly readonly properties.
Quick: Can you assign a new object to a readonly property if the property is an object? Commit to your answer.
Common Belief:Readonly properties make the entire object immutable, including nested objects.
Tap to reveal reality
Reality:Readonly only prevents reassigning the property itself; nested objects inside can still be changed unless they are also readonly or frozen.
Why it matters:Assuming deep immutability causes bugs when nested data changes unexpectedly.
Quick: Does marking a property readonly make the whole object readonly? Commit to your answer.
Common Belief:If one property is readonly, the entire object becomes readonly.
Tap to reveal reality
Reality:Only the marked properties are readonly; other properties remain writable unless also marked readonly.
Why it matters:Misunderstanding this leads to incorrect assumptions about object mutability and can cause errors in code design.
Quick: Does readonly affect type compatibility in TypeScript? Commit to your answer.
Common Belief:Readonly properties do not affect whether types are compatible or assignable.
Tap to reveal reality
Reality:Readonly properties are part of the type shape and affect compatibility; a readonly property cannot be assigned to a non-readonly property and vice versa without error.
Why it matters:Ignoring this can cause confusing type errors or unsafe assignments in large codebases.
Expert Zone
1
Readonly properties only protect against reassignment, not mutation of nested objects, so deep immutability requires additional patterns.
2
Readonly modifiers interact with mapped types and conditional types, enabling complex type transformations that enforce immutability selectively.
3
Readonly properties can affect type inference and overload resolution subtly, influencing how TypeScript narrows types in functions and classes.
When NOT to use
Readonly properties are not suitable when you need to modify data frequently or when runtime immutability is required. In those cases, consider using immutable data libraries like Immutable.js or runtime methods like Object.freeze for true 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 enforce immutability contracts across large codebases, improving maintainability and reducing bugs.
Connections
Immutable data structures
Readonly properties are a compile-time step towards immutability, while immutable data structures enforce it at runtime.
Understanding readonly helps grasp the foundation of immutability, which is critical in functional programming and state management.
Functional programming
Functional programming emphasizes immutability, and readonly properties support this by preventing unintended data changes.
Knowing readonly properties aids in writing pure functions and predictable code, key goals of functional programming.
Access control in object-oriented programming
Readonly properties act like 'get-only' accessors, controlling how data can be modified, similar to private or protected modifiers.
Recognizing readonly as a form of access control deepens understanding of encapsulation and data safety.
Common Pitfalls
#1Trying to change a readonly property after initialization.
Wrong approach:interface Person { readonly name: string; } const p: Person = { name: 'Alice' }; p.name = 'Bob'; // error here
Correct approach:interface Person { readonly name: string; } const p: Person = { name: 'Alice' }; // Do not assign p.name again; create a new object if needed
Root cause:Misunderstanding that readonly means 'set once and never change' leads to attempts to reassign.
#2Assuming readonly makes nested objects immutable.
Wrong approach:interface Person { readonly address: { city: string }; } const p: Person = { address: { city: 'NY' } }; p.address.city = 'LA'; // allowed, but unexpected
Correct approach:interface Address { readonly city: string; } interface Person { readonly address: Address; } const p: Person = { address: { city: 'NY' } }; // p.address.city = 'LA'; // error if Address.city is readonly
Root cause:Confusing shallow readonly with deep immutability causes unexpected mutations.
#3Expecting readonly to prevent runtime changes without extra measures.
Wrong approach:class Person { readonly name: string = 'Alice'; } const p = new Person(); p.name = 'Bob'; // no runtime error, only compile-time warning
Correct approach:Use Object.freeze or proxies for runtime immutability: const p = Object.freeze({ name: 'Alice' }); p.name = 'Bob'; // runtime error or ignored
Root cause:Believing TypeScript readonly enforces runtime immutability leads to false security.
Key Takeaways
Readonly properties let you set a value once and prevent changes, helping avoid bugs from accidental data modification.
They are enforced only at compile time by TypeScript and do not affect runtime behavior unless combined with JavaScript features.
Readonly applies shallowly; nested objects inside readonly properties can still be changed unless they are also readonly or frozen.
Using readonly properties improves code safety, clarity, and maintainability, especially in large or shared codebases.
Understanding readonly's interaction with type compatibility and mapped types unlocks advanced TypeScript patterns for immutability.