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

declare class Key<T extends string = string, V = any> {
    readonly name: T;
    constructor(name: T);
    asType(implementation: any): V;
}

type InjectableOrBindable<T = any, V extends ReadonlyArray<Key> = []> = T & {
    __dependencies__: V;
};

type ParamsToKeys<P extends readonly any[]> = {
    [K in keyof P]: Key<string, P[K]>;
};

type ResolvedDependencies<T extends readonly Key[]> = T extends readonly [infer First, ...infer Rest] ? First extends Key ? [
    ReturnType<First["asType"]>,
    ...(Rest extends readonly Key[] ? ResolvedDependencies<Rest> : [])
] : never : [];

type Subset<T extends any[]> = T extends [infer First, ...infer Rest] ? [] | [First, ...Subset<Rest>] : [];

declare class BoundFunction<T extends (...args: any[]) => any, U extends readonly Key[], V extends (...args: [...ResolvedDependencies<U>, ...Parameters<T>]) => ReturnType<T>> {
    readonly dependencies: U;
    private key;
    private bindable;
    constructor(key: Key<string, T>, bindable: InjectableOrBindable<V, U>);
    resolve(resolvedDependencies: ResolvedDependencies<U>): T;
}

declare class Constant<V> {
    private readonly value;
    constructor(value: V);
    resolve(): V;
}

interface Injectable {
    resolve(...args: any[]): any;
}

declare class Container<T extends Record<string, Injectable>> {
    readonly injectables: T;
    private resolvedInjectables;
    private singletons;
    private singletonPlaceholders;
    constructor(injectables: T);
    get<K extends keyof T & string>(key: Key<K, ReturnType<T[K]["resolve"]>>): ReturnType<T[K]["resolve"]>;
    private resolveWithDependencyGraph;
    private isSingleton;
    private wasPreviouslyResolved;
    private completesSingletonOnlyDependencyCycle;
    private flagResolved;
}

declare class InjectedClass<T, U extends Constructor<T>, V extends ParamsToKeys<ConstructorParameters<U>>> {
    readonly dependencies: V;
    readonly isSingleton: boolean;
    private injectable;
    private key;
    constructor(key: Key<string, T>, injectable: InjectableOrBindable<U, V>, isSingleton: boolean);
    resolve(resolvedDependencies: ResolvedDependencies<V>): T;
}

declare class ContainerBuilder<T extends Record<string, Injectable>> {
    readonly injectables: T;
    private constructor();
    static createBuilder(): ContainerBuilder<{}>;
    registerClass<T extends string, S, U extends Constructor<S>, V extends ParamsToKeys<ConstructorParameters<U>>>(key: Key<T, S>, injectable: InjectableOrBindable<U, V>): ContainerBuilder<this["injectables"] & { [K in T]: InjectedClass<S, U, V>; }>;
    registerSingleton<T extends string, S, U extends Constructor<S>, V extends ParamsToKeys<ConstructorParameters<U>>>(key: Key<T, S>, injectable: InjectableOrBindable<U, V>): ContainerBuilder<this["injectables"] & { [K in T]: InjectedClass<S, U, V>; }>;
    registerFunction<T extends string, S extends (...args: any[]) => any, U extends readonly Key[], V extends (...args: [...ResolvedDependencies<U>, ...Parameters<S>]) => ReturnType<ReturnType<S>>>(key: Key<T, S>, bindable: InjectableOrBindable<V, U>): ContainerBuilder<this["injectables"] & { [K in T]: BoundFunction<S, U, V>; }>;
    registerConstant<T extends string, V>(key: Key<T, V>, value: V): ContainerBuilder<this["injectables"] & { [K in T]: Constant<V>; }>;
    build(): Container<T>;
    private register;
}

type ForTypeReturnType<S extends string, T extends Record<string, Key>, U> = Keys<T & {
    [K in S]: Key<S, U>;
}>;
type ForType<S extends string, T extends Record<string, Key>> = {
    forType: <U>() => ForTypeReturnType<S, T, U>;
};
declare class Keys<T extends Record<string, Key>> {
    readonly keys: T;
    private constructor();
    static createKeys(): Keys<{}>;
    addKey<S extends string>(name: S): ForType<S, this["keys"]>;
}

declare function bind<T extends (...args: any[]) => any, V extends ParamsToKeys<Subset<Parameters<T>>>>(func: T, deps: V): InjectableOrBindable<T, V>;

declare function inject<T, U extends Constructor<T>, V extends ParamsToKeys<ConstructorParameters<U>>>(cls: U, dependencies: V): InjectableOrBindable<U, V>;

export { ContainerBuilder, Keys, bind, inject };
