TypeScript
Completed
-
Section 1: Getting Started
-
Section 2: Basic Types
-
Section 3: Functions and Objects
-
Section 4: Advanced Types
-
Section 5: Object-Oriented TypeScript
-
Section 6: Working with Modules
-
Section 7: Tooling and Practice
-
Section 8: Type-Level Programming
-
Section 9: TypeScript with Backends
-
Section 10: Testing Typed Code
-
Section 11: Data Validation with Types
-
Section 12: Practical Projects
-
Section 13: Compiler Internals
-
Section 14: Configuration Deep Dive
-
Section 15: Enums, Symbols, and Special Types
-
Section 16: Working with Async Code
-
Section 17: TypeScript and the DOM
-
Section 18: Advanced Generics Practice
-
Section 19: Working with Third-Party Types
-
Section 20: Monorepo and Large-Scale Practices
-
Section 21: Common Pitfalls and Best Practices
-
Section 22: Interview Practice
-
Section 23: Handbook: Narrowing In Depth
-
Section 24: Handbook: Object Types In Depth
-
Section 25: Handbook: Classes In Depth
-
Section 26: Handbook: Modules In Depth
-
Section 27: Handbook: Declaration Files In Depth
-
Section 28: JSX and Namespaces
-
Section 29: Compiler Configuration Reference
-
Section 30: More Practice Exercises
-
Section 31: Handbook: Everyday Types Deep Dive
-
Section 32: Utility Types Full Reference
-
Section 33: Decorators Reference
-
Section 34: Mixins and Advanced OOP Patterns
-
Section 35: Iterators and Generators Typing
-
Section 36: More Type-Level Programming Practice
-
Section 37: TypeScript Ecosystem Tools
-
Section 38: TypeScript with Testing Frameworks
-
Section 39: TypeScript for Library Authors
-
Section 40: More Interview Practice
-
Section 41: Handbook: Functions In Depth
-
Section 42: Handbook: Type Manipulation Deep Dive
-
Section 43: More Real-World Patterns
-
Section 44: TypeScript Release Notes Highlights
-
Section 45: Final Practice and Review
21: Readonly Properties
By now, you're probably used to using const to keep your variables from being reassigned. But when you're dealing with classes or interfaces, const doesn't work on individual properties. That's where readonly comes in.
Why use readonly instead of just avoiding a setter?
In a plain JavaScript class, you might just not provide a setter method, or you might use a private field with a getter. But in TypeScript, readonly is a signal to the compiler. It tells anyone using your class—including your future self—that this property is meant to be set once and then left alone.
Take a User account system, for example. You want the userId to be immutable because changing a primary key mid-session is a recipe for a database nightmare.
class UserAccount {
readonly userId: string;
username: string;
constructor(id: string, name: string) {
this.userId = id;
this.username = name;
}
}
const user = new UserAccount("usr_12345", "DevDan");
user.username = "DanTheCoder"; // This is fine
user.userId = "usr_99999"; // Error: Cannot assign to 'userId' because it is a read-only property.
The beauty here is that the error happens in your IDE, not at runtime. You don't have to wait for a bug to hit production to realize you accidentally overwrote a critical ID.
Wait, how is this different from using const?
This is a common point of confusion. Think of it this way: const is for variables; readonly is for properties.
You can't put const inside a class definition. If you try, TypeScript will scream at you. readonly is specifically designed for the members of a class or the fields of an interface. I usually use readonly for "configuration" style data—things that are determined at the moment an object is created and should stay that way for the rest of the object's lifecycle.
If I mark an array as readonly, can I still push items into it?
Here is the "gotcha" I always warn people about. If you mark a property as readonly, you can't reassign the property to a new array, but you can still mutate the contents of that array.
interface AppConfig {
readonly plugins: string[];
}
const config: AppConfig = {
plugins: ["Auth", "Logging"]
};
config.plugins = ["NewPlugin"]; // Error: Readonly property
config.plugins.push("Analytics"); // This actually WORKS.
If you want the actual contents of the array to be immutable, you need to use the ReadonlyArray type (or the readonly string[] shorthand). I highly recommend doing this if you're building a system where data integrity is critical. It forces you to create a new array instead of mutating the existing one, which is a much safer pattern in modern JS/TS.
Can I ever change a readonly property after the class is initialized?
Short answer: No. The only place a readonly property can be assigned is inside the constructor. Once the constructor finishes executing, that property is locked down.
If you find yourself wanting to change a readonly property later, you're probably dealing with a state change that shouldn't be readonly, or you should be creating a new instance of the object entirely. Don't try to "cheat" the system by casting to any just to change a value; that defeats the entire purpose of using TypeScript in the first place.
📋 Practical Task
Exercise: Securing the GameSession Configuration
You are building a game engine. Each GameSession should have a unique sessionId and a difficulty setting that cannot be changed once the game has started. However, the currentScore should be updateable as the player progresses.
Your Task:
- Create a
GameSessionclass. - Add a
sessionIdproperty (string) and adifficultyproperty (string). Both must be immutable after instantiation. - Add a
currentScoreproperty (number) that can be modified. - Implement a constructor to initialize all three values.
- Instantiate the class and attempt to change the
sessionIdto verify that TypeScript throws an error.
There are no comments for now.