Search code examples
typescripttypescript-decoratorclass-decorator

Is there a way to add a dynamic named property in a class at runtime and have it known to typescript


I have a decorator that modifies a class and adds some methods to the class A. While using the instance these methods are not in autocomplete. I also get compiler errors since these properties are not known.

I know that decorators can't change the type of a class, it is not supported yet. And I'm not inclined towards using mixins since this code is going to be wrapped in a library and used by others. So it would be simpler if the users can use the @ notation.

Example:

function TypeChangeDecorator(constructor: any) {
  const keys = Reflect.getMetadata(SOME_KEY_SYMBOL, TypeB);
  return class extends constructor {
    private someClass: TypeA;
    
    constructor(...args: any[]) {
      super(...args);
      
      for(key in keys) {
        this[`set${key}`] = () => {} // some method
      }
    }

    someMethod() {
      // implementation here
    }
  }
}

@TypeChangeDecorator
class A {}

I know it might not be possible but I need to access methods someMethod and set${key}. Have the compiler not give me an error while accessing these and if possible have them in autocomplete.

PS: from what I have found this is not possible yet, but if anyone has some insights on this, it would be appreciated.


Solution

  • For anyone looking for a solution and not use mixins, there is something called as the interface trick.

    interface PropertiesAddedByDecorator {
      someMethod(): any;
    }
    
    interface A extends PropertiesAddedByDecorator;
    
    @ TypeChangeDecorator
    class A {}
    
    // now when you do
    const inst = new A();
    inst.someMethod() // no error and autocomplete support
    

    The part set${key}, is not possible, you can use Record<string, Function> to avoid compiler errors.

    Also note the comment from jcalz, we can do declare someMethod: () => {} inside A instead of declaring interfaces.