I have the following TypeScript snippet:
class Jedi {
private readonly name: string = 'Skywalker';
constructor(name: string) {
this.name = name; //Why I am able to modify name.
}
toString():string {
return this.name;
}
}
var jedi = new Jedi('Kenobi');
console.log(jedi.toString()); //Kenobi
As you can see in the code, I have declared name
property as readonly
. As per my knowledge to declare properties as constant we use readonly
in TypeScript. Once we declare it with initialization, we can not modify it.
But as you can see in the constructor
it's being modified. Also TypeScript compiler also does not warn about it. Not sure whether it's a bug in TypeScript or it's intentional. Can anyone please explain?
You can use mapping modifiers to change a readonly property to mutable in TypeScript, e.g. -readonly [Key in keyof Type]: Type[Key] . You can remove the readonly modifier by prefixing the readonly keyword with a minus - .
You can initialize a ReadOnly property in the constructor or during object construction, but not after the object is constructed.
TypeScript includes the readonly keyword that makes a property as read-only in the class, type or interface. Prefix readonly is used to make a property as read-only. Read-only members can be accessed outside the class, but their value cannot be changed.
Conclusion # The error "Cannot assign to 'X' because it is a read-only property" occurs when we try to change the value of a read-only property in a class or an object. To solve the error, remove the readonly modifier or use a type assertion to change the value of the property.
The docs only say
Readonly properties must be initialized at their declaration or in the constructor. (Source)
But as Romain pointed out there is a little more info in the TypeScript 2.0 release notes:
Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed. (Source)
But what confuses me is the compiled output. See an example on the TS Playground.
As you can see the initially (readonly) property is overwritten without any warning from TS at compile time. I guess this is intentional, because the language behaves the same all the time and there are less edge cases.
EDIT: There is also a "sugarized" way to initialize a class
with properties in TypeScript (I guess you know that but anyway):
Adding public
/private
to arguments of the constructor signature will initialize those arguments as class properties. So in your example:
class Jedi {
constructor(
private readonly name: string = 'Skywalker'
) {}
toString():string {
return this.name;
}
}
And if let TypeScript compile the above code, it actually works as expected (or at least what I would expect). There is another Link to the Playground.
tl;dr; Initializing class properties inside the constructor signature via public
/private
will set the default value if none is given. This does not work if you set class properties "by hand" (this.name = name
). The later might still not be a bug but rather an intentional behaviour, because you explicitly set the class property.
You can avoid this issue (or at least have TypeScript scream at you) by enabling noImplicitAny
and strictNullChecks
. This way TypeScript will let you know that your class property might be undefined
.
What I would suggest is not using readonly
property if you want to have immutability. Not only is the above mentioned kinda weird behaviour, some less experienced developers might also think that a readonly
array/object is really fully frozen/read only where it isn't. Rather use something like ImmutableJS.
From the TypeScript documentation:
Read-only properties may have initializers and may be assigned to in constructors within the same class declaration, but otherwise assignments to read-only properties are disallowed.
More about the readonly
keyword
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With