I'm converting some legacy JS code to TS, and have run into an interesting typing situation. There is a module system that is loading class instances at runtime onto named properties of an instance of another class, and am trying to figure out how to type the object:
class Module {
public name: string;
}
class ThisModule extends Module {
public name = 'thisModule';
public log = () => {
console.log('ThisModule called!');
};
}
class ThatModule extends Module {
public name = 'thatModule';
public log = () => {
console.log('ThatModule called!');
};
}
interface IModule extends Module {}
type ModulesInput = Module[];
class Container {
public init = (modules: ModulesInput) => {
for (const module of modules) {
this[module.name] = module;
}
};
}
const thisModule = new ThisModule();
const thatModule = new ThatModule();
const container = new Container<ThisModule | ThatModule >();
const modules: ModulesInput = [
thisModule,
thatModule,
];
container.init(modules);
// Should not throw TS error
container.thisModule.log();
container.thatModule.log();
// Should throw error ("Property anythingElse does not exist on Container")
container.anythingElse.log();
The problem is that TypeScript is not recognizing that container.thisModule
or container.thatModule
should exist. It says TS2339: Property 'thisModule' does not exist on type 'Container'.
(and also for thatModule
, the same).
Is there a way to type this system? I have had some limited success so far adding multiple generics to the Container
class (e.g. type keys = 'thisModule' | 'thatModule'
and type ModulesInUse = ThisModule | ThatModule
), but can TypeScript discover the names from the classes and dynamically learn that it should expect those keys on the container
object to have types of their respective classes?
Thanks in advance!