type Constructor<T = any> = {
    new (...args: any[]): T;
};

type Token<T = any> = Constructor<T> | object;

interface Resolver {
    resolve<T>(token: Token<T>): T;
}

type FactoryFn<T> = (resolver: Resolver, resolutionChain: Token[]) => T;

declare enum Scope {
    transient = 0,
    resolution = 1
}

type RegistrationOptions = {
    scope: Scope;
};

interface Registry {
    register<T>(ctor: Constructor<T>, options: RegistrationOptions): this;
    registerAlias<T1, T2 extends T1>(from: Token<T1>, to: Token<T2>): this;
    registerFactory<T>(token: Token<T>, factory: FactoryFn<T>): this;
    registerFactory<T>(token: Token<T>, factory: FactoryFn<T>, options: RegistrationOptions): this;
    registerSingleton<T>(ctor: Constructor<T>): this;
    registerSingleton<T>(ctor: Constructor<T>, instance: T): this;
}

declare abstract class Container implements Registry, Resolver {
    static create(): Container;
    abstract createChildContainer(): Container;
    abstract register<T>(ctor: Constructor<T>, options: RegistrationOptions): this;
    abstract registerAlias<T1, T2 extends T1>(from: Token<T1>, to: Token<T2>): this;
    abstract registerFactory<T>(token: Token<T>, factory: FactoryFn<T>): this;
    abstract registerFactory<T>(token: Token<T>, factory: FactoryFn<T>, options: RegistrationOptions): this;
    abstract registerSingleton<T>(ctor: Constructor<T>): this;
    abstract registerSingleton<T>(ctor: Constructor<T>, instance: T): this;
    abstract resolve<T>(token: Token<T>): T;
}

declare function injectable(): (target: any) => void;

export { Container, Scope, injectable };
