Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: Why am I able to modify `readonly` property in constructor?

Tags:

typescript

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?

like image 963
Hitesh Kumar Avatar asked Feb 13 '17 09:02

Hitesh Kumar


People also ask

How do I change a read-only property in TypeScript?

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 - .

Can readonly property be set in constructor?

You can initialize a ReadOnly property in the constructor or during object construction, but not after the object is constructed.

Is readonly property available in TypeScript?

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.

How do you set a read-only property?

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.


2 Answers

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 readonlyproperty 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.

like image 88
Sebastian Sebald Avatar answered Nov 16 '22 04:11

Sebastian Sebald


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

like image 30
Romain Avatar answered Nov 16 '22 02:11

Romain