Search code examples
typescriptpass-by-referencepass-by-name

Typescript pass property by reference


I have a Typescript class with 4 different properties, like this:

class MyClass {
     private x: number;
     private y: number;
     private z: number;
     private w: number;
}

I want to create four functions that increment these properties:

   incrementX() { this.x++; }
   incrementY() { this.y++; )
   ...

However, I don't want the increment logic (++) to be duplicated, I want to place it in one function. If Typescript had ref parameters like C#, I would do something like:

   incrementX() { this.increment(ref this.x); }
   increment(p: ref number) { p++; }

Typescript does not support passing by reference. The non type-safe way of implementing this is:

   incrementX() { this.increment("x"); }
   increment(p: string) {
       const self = this as any;
       self[p]++;
   }

It's not type-safe. I can easily call increment('not-a-property') without getting an error from the compiler. I've added a runtime check to make sure self[p] is indeed a number, but I still want something the compiler can catch.

Is there a type-safe way of implementing this?

Note: obviously my actual code doesn't increment numbers, but rather does something quite elaborate - not on numbers but on another class type.


Solution

  • You could use keyof and number extends maybe? To allow passing only keys of the class which are numbers.

    Playground here

    class MyClass {
      public a: number = 0;
      public b: number = 0;
      public c: string = "";
    
      public increment(
         key: {
          [K in keyof MyClass]-?: number extends MyClass[K] ? K : never
        }[keyof MyClass]
      ) {
        this[key]++;
      }
    }
    
    const test = new MyClass();
    
    test.increment("a");
    test.increment("b");
    test.increment("c"); // fail
    test.increment("d"); // fail