In the previous discussion, it was pointed out that TypeScript does not aim to generate JavaScript code that relies on the type system. This means that there won't be any runtime support for automatically achieving your desired functionality.
However, you can leverage TypeScript to maintain a collection of types in a manner similar to what you need. One approach could be using class decorators, such as:
interface IControlPanel {
// define methods or attributes here
doAThing(): void;
}
// create a registry for expected types
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;
}
}
Instead of explicitly stating that a class 'implements IControlPanel', you can now use the '@IControlPanel.register' decorator:
@IControlPanel.register
class BasicControlPanel {
doAThing() { }
}
@IControlPanel.register
class AdvancedControlPanel {
doAThing() { }
}
If you attempt to register a class that does not adhere to the IControlPanel
interface, an error will occur:
// error, BadControlPanel is missing the required doAThing method
@IControlPanel.register
class BadControlPanel {
doNothing() { }
}
You can now utilize the registry as needed, with some limitations:
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();
}
};
Although constructors are stored instead of strings as requested for instantiation, you can access the constructor's name
property to get the string representation. Additionally, all classes must accept the same constructor argument types.
See Stackblitz example