How can I change a readonly property in TypeScript?

You could make use of the improved mapped type modifiers since Typescript 2.8.

For example, let's say that UI layer (and all others except persistence layer) shall only get a readonly version of your domain entity. Persistence layer is a special case, since it somehow must know how to copy all internals into the database. In order to do so, we don't want to make defensive copies everytime and just use the readonly typescript modifier for that purpose.

Your readonly entity would be:

class Immutable {
    constructor(
        public readonly myProp: string ) {}
}

The mutable type of your entity:

type Mutable = {
     -readonly [K in keyof Immutable]: Immutable[K] 
}

Note the special -readonly syntax to remove the flag (also works with optionals).

In one limited place (here the persistence layer) we can convert Immutable to Mutable by doing:

let imm = new Immutable("I'm save here")
imm.myProp = "nono doesnt work. and thats good" // error
let mut: Mutable = imm  // you could also "hard" cast here: imm as unknown as Mutable
mut.myProp = "there we go" // imm.myProp value is "there we go"

Hope that helps.


There are actually 3 ways I know of. If you have a class like this:

class GraphNode {
    readonly _parent: GraphNode;
    add(newNode: GraphNode) { /* ...etc... */ }
}
var node = new GraphNode();

In the add() function you could do either:

  1. newNode[<any>'_parent'] = this; - Works, but BAD IDEA. Refactoring will break this.

    Update: Seems newNode['_parent'] = this; will work just fine now without <any> in newer versions of TypeScript, but refactoring will still break it.

  2. (<{_parent: GraphNode}>newNode)._parent = this; - Better than 1 (not the best), and although refactoring breaks it, at least the compiler will tell you this time (since the type conversion will fail).
  3. BEST: Create an INTERNAL interface (used by yourself only):

    interface IGraphObjectInternal { _parent: GraphNode; }
    class GraphNode implements IGraphObjectInternal {
        readonly _parent: GraphNode;
        add(newNode: GraphNode) { /* ...etc... */ }
    }
    

    Now you can just do (<IGraphObjectInternal>newNode)._parent = this; and refactoring will also work. The only caveat is that if you export your class from a namespace (the only reason to use an internal interface IMO) you'll have to export the interface as well. For this reason, I sometimes will use #2 to completely lock down internals where there's only one place using it (and not advertise to the world), but usually #3 if I need to have many properties to work with referenced in many other locations (in case I need to refactor things).

You may notice I didn't talk about getters/setters. While it is possible to use only a getter and no setter, then update a private variable, TypeScript does not protect you! I can easily do object['_privateOrProtectedMember'] = whatever and it will work. It does not work for the readonly modifier (which was in the question). Using the readonly modifier better locks down my properties (as far as working within the TypeScript compiler is concerned), and because JavaScript doesn't have a readonly modifier, I can use various methods to update them with workarounds on the JavaScript side (i.e. at runtime). ;)

Warning: As I said, this only works within TypeScript. In JavaScript people can still modify your properties (unless you use getters only with non-exposed properties).

Update

Since typescript 2.8 you can now remove the readonly modifiers:

type Writeable<T> = { -readonly [P in keyof T]: T[P] };

and also the optional modifier:

type Writeable<T> = { -readonly [P in keyof T]-?: T[P] };

More here: Improved control over mapped type modifiers

Tags:

Typescript