How can I change a readonly property in TypeScript?

后端 未结 6 1914
眼角桃花
眼角桃花 2021-01-04 02:43

I want to be able to make readonly properties (not getters) for users of my class, but I need to update them internally; is there a way to do this and allow to

6条回答
  •  生来不讨喜
    2021-01-04 03:24

    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['_parent'] = this; - Works, but BAD IDEA. Refactoring will break this.

      Update: Seems newNode['_parent'] = this; will work just fine now without 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 (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 = { -readonly [P in keyof T]: T[P] };
    

    and also the optional modifier:

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

    More here: Improved control over mapped type modifiers

提交回复
热议问题