type Constructor = new (...args: S[]) => T; type Definition = { deps?: Dependency[], calls?: Call[] }; type Dependency = any; type Call = (Class: T) => void; export class Resolver { private definitions = new Map(); private container = new WeakMap(); // to detect circular depenendcies: private seen = new WeakMap(); constructor(definitions: Iterable) { this.definitions = new Map(definitions); for (const [ Class ] of this.definitions) { this.get(Class); } } public get(Class: Constructor): T { if (this.container.has(Class)) { return this.container.get(Class); } if (this.seen.has(Class)) { throw new Error(`Detected circular dependency: ${Class}`); } this.seen.set(Class, null); const { deps: dependencies = [], calls = [] }: Definition = this.definitions.get(Class) || {}; const instance: T = new Class( ...dependencies.map((dependency: Dependency) => this.get(dependency)) ); calls.forEach((call: Call) => call(instance)); this.container.set(Class, instance); return instance; } }