export type Provider = (manager: Manager) => T; export interface Identifier extends Symbol {} export class Manager { private providers = new Map(); private instances = new Map(); private resolving = new Set(); register(identifier: Identifier, provider: Provider) { if (this.providers.has(identifier)) { throw new Error('This identifier is already used'); } this.providers.set(identifier, provider); } inject(identifier: Identifier): T { if (!this.providers.has(identifier)) { throw new Error('This identifier has not been registred'); } if (this.instances.has(identifier)) { return this.instances.get(identifier) as T; } if (this.resolving.has(identifier)) { throw new Error('Circular dependency detected'); } this.resolving.add(identifier); const declaration = this.providers.get(identifier) as Provider; const instance = declaration(this); this.instances.set(identifier, instance); this.resolving.delete(identifier); return instance; } }