implements typed identifiers
This commit is contained in:
@@ -1,57 +1,37 @@
|
||||
export type Provider<T = unknown> = (manager: DependencyManager) => T;
|
||||
export type Provider<T = unknown> = (manager: Manager) => T;
|
||||
export interface Identifier<T = unknown> extends Symbol {}
|
||||
|
||||
export type ProviderGroup = {
|
||||
[key: string]: Provider | ProviderGroup;
|
||||
};
|
||||
export class Manager {
|
||||
private providers = new Map<Identifier, Provider>();
|
||||
private instances = new Map<Identifier, unknown>();
|
||||
private resolving = new Set<Identifier>();
|
||||
|
||||
export class DependencyManager {
|
||||
private declarations = new Map<unknown, Provider>();
|
||||
private instances = new Map();
|
||||
private resolving = new Set();
|
||||
|
||||
register(dependency: Provider): void;
|
||||
register(module: ProviderGroup): void;
|
||||
register(dependencyOrModule: Provider | ProviderGroup): void;
|
||||
register(dependencyOrModule: Provider | ProviderGroup) {
|
||||
if (this.declarations.has(dependencyOrModule)) {
|
||||
throw new Error('This dependency has already been registred');
|
||||
register<T>(identifier: Identifier<T>, provider: Provider<T>) {
|
||||
if (this.providers.has(identifier)) {
|
||||
throw new Error('This identifier is already used');
|
||||
}
|
||||
|
||||
if (typeof dependencyOrModule === 'function') {
|
||||
this.declarations.set(dependencyOrModule, dependencyOrModule);
|
||||
return;
|
||||
}
|
||||
|
||||
const module = dependencyOrModule;
|
||||
for (const key in module) {
|
||||
this.register(module[key]);
|
||||
}
|
||||
this.providers.set(identifier, provider);
|
||||
}
|
||||
|
||||
resolve<T>(dependency: Provider<T>): T {
|
||||
if (!this.declarations.has(dependency)) {
|
||||
throw new Error('This dependency has not been registred');
|
||||
inject<T>(identifier: Identifier<T>): T {
|
||||
if (!this.providers.has(identifier)) {
|
||||
throw new Error('This identifier has not been registred');
|
||||
}
|
||||
|
||||
if (this.instances.has(dependency)) {
|
||||
return this.instances.get(dependency) as T;
|
||||
if (this.instances.has(identifier)) {
|
||||
return this.instances.get(identifier) as T;
|
||||
}
|
||||
|
||||
if (this.resolving.has(dependency)) {
|
||||
if (this.resolving.has(identifier)) {
|
||||
throw new Error('Circular dependency detected');
|
||||
}
|
||||
|
||||
this.resolving.add(dependency);
|
||||
const instance = this.instanciate(dependency);
|
||||
this.resolving.delete(dependency);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
private instanciate<T>(dependency: Provider<T>): T {
|
||||
const declaration = this.declarations.get(dependency) as Provider<T>;
|
||||
this.resolving.add(identifier);
|
||||
const declaration = this.providers.get(identifier) as Provider<T>;
|
||||
const instance = declaration(this);
|
||||
this.instances.set(dependency, instance);
|
||||
this.instances.set(identifier, instance);
|
||||
this.resolving.delete(identifier);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user