Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript - Get all implementations of interface

Tags:

typescript

I'm searching for a way to get a list of all classes implementing a certain interface in Typescript.

In .Net for example, you can do this using reflection but i cant find any information about doing the same thing in Typescript.

Code example of what i want to do:

interface IControlPanel { }
class BasicControlPanel implements IControlPanel { }
class AdvancedControlPanel implements IControlPanel { }
window.onload = () =>
{
    var controlPanels = IControlPanel.GetImplementations();
    for (var x = 0; x < controlPanels.length; x++)
    {
        document.write(controlPanels[x] + ", "); //outputs: BasicControlPanel, AdvancedControlPanel,
    }
};

Even better would ofcourse be if the classes are easy to instantiate.

like image 288
DOOMDUDEMX Avatar asked Nov 02 '17 18:11

DOOMDUDEMX


1 Answers

As I mentioned in the comments, it's a stated non-goal of TypeScript (see non-goal #5) to emit JavaScript that is dependent on the type system, so there will be nothing at runtime you can use automatically to do what you want.

You can, of course, use TypeScript to help you maintain a registry of types that can be used almost exactly how you want. I'd suggest using class decorators, like so:

interface IControlPanel {
  // add some methods or something to distinguish from {}
  doAThing(): void;
}

// add a registry of the type you expect
namespace IControlPanel {
  type Constructor<T> = {
    new(...args: any[]): T;
    readonly prototype: T;
  }
  const implementations: Constructor<IControlPanel>[] = [];
  export function GetImplementations(): Constructor<IControlPanel>[] {
    return implementations;
  }
  export function register<T extends Constructor<IControlPanel>>(ctor: T) {
    implementations.push(ctor);
    return ctor;
  }
}

Now, instead of declaring that the class implements IControlPanel, you use @IControlPanel.register:

@IControlPanel.register
class BasicControlPanel {
  doAThing() { }
}

@IControlPanel.register
class AdvancedControlPanel {
  doAThing() { }
}

If you try to register a class that does not implement IControlPanel, you will get an error:

// error, doAThing is missing from BadControlPanel
@IControlPanel.register
class BadControlPanel {
  doNothing() { }
}

Now you can use the registry the way you want, mostly:

window.onload = () => {
  var controlPanels = IControlPanel.GetImplementations();
  for (var x = 0; x < controlPanels.length; x++) {
    document.write(controlPanels[x].name + ", ");
    const panel = new controlPanels[x]();
    panel.doAThing();
  }
};

I say "mostly" because I've stored constructors, not strings, since you wanted a way to instantiate them. You can get the name property of the constructor for the string. And you can instantiate the classes, assuming they all take the same constructor argument types.

Hope that helps; good luck!

Stackblitz example

like image 55
jcalz Avatar answered Oct 06 '22 01:10

jcalz