Developapa


Typescript decorator to call a method only once

May 01, 2023

Sometimes you want methods on a class just to be called once. Cases like this could be for example some initialization logic, that does not fit into a constructor because the value is set at a later point in time but once it’s set, it is not allowed to be overwritten.

What I did in the past was just create a variable outside called isInitialized that defaults to false. And once the function was called set it to true. Simplified this looked something like this:

class MyTestClass {
  isInitialized: boolean = false;
  
  public init(): void {
    if (this.isInitialized) {
      return;
    }
    this.isInitialized = true;
    // do my init logic here
  }
}

With a TypeScript decorator this can be simplified and extracted in a decorator function.

export function callFunctionOnlyOnce() {
  return (target: unknown, key: string, descriptor: PropertyDescriptor) => {
    const originalMethod: any = descriptor.value;
    let isInitialized: boolean = false;

    descriptor.value = function (...args: Array<unknown>): void {
      if (isInitialized || !originalMethod) {
        return;
      }
      isInitialized = true;

      originalMethod.apply(this, args);
    };
  };
}

class MyTestClass {
  @callFunctionOnlyOnce()
  public init(): void {
    //do init logic here
  }
}

If you want to use this on a setter function you need to replace the descriptor.value with a descriptor.set

function callSetterOnlyOnce() {
  return (target: unknown, key: string, descriptor: PropertyDescriptor) => {
    const originalMethod: any = descriptor.set;
    let isInitialized: boolean = false;

    descriptor.set = function (...args: Array<unknown>): void {
      if (isInitialized || !originalMethod) {
        return;
      }
      isInitialized = true;

      originalMethod.apply(this, args);
    };
  };
}

class MyTestClass {
  @callSetterOnlyOnce()
  set myValue(newValue: number): void {
    //do init logic here
  }
}

If you want to see this in action you can check out this Stackblitz


Personal Blog written by Nicolas Gehlert, software developer from Freiburg im Breisgau. Developer & Papa. Github | Twitter

Add a comment

Comments

There are no comments available for this blog post yet

© 2024, Nicolas Gehlert