import { DenyInputKeys, Factory, IDIContainer, ResolvedDependencies, StringLiteral } from "./types";
/**
 * Dependency injection container
 */
export declare class DIContainer<ContainerResolvers extends ResolvedDependencies = {}> {
    private resolvers;
    private resolvedDependencies;
    private context;
    /**
     * Adds new dependency resolver to the container. If dependency with given name already exists it will throw an error.
     * Use update method instead. It will override existing dependency.
     *
     * @param name
     * @param resolver
     */
    add<N extends string, R extends Factory<ContainerResolvers>>(name: StringLiteral<DenyInputKeys<N, keyof ContainerResolvers>>, resolver: R): IDIContainer<ContainerResolvers & {
        [n in N]: ReturnType<R>;
    }>;
    /**
     * Updates existing dependency resolver. If dependency with given name does not exist it will throw an error.
     * In most cases you don't need to override dependencies and should use add method instead. This approach will
     * help you to avoid overriding dependencies by mistake.
     *
     * You may want to override dependency if you want to mock it in tests.
     *
     * @param name
     * @param resolver
     */
    update<N extends keyof ContainerResolvers, R extends Factory<ContainerResolvers>>(name: StringLiteral<N>, resolver: R): IDIContainer<{
        [P in Exclude<keyof ContainerResolvers, N>]: ContainerResolvers[P];
    } & {
        [n in N]: ReturnType<R>;
    }>;
    /**
     * Checks if dependency with given name exists
     * @param name
     */
    has(name: string): boolean;
    /**
     * Resolve dependency by name. Alternatively you can use property access to resolve dependency.
     * For example: const { a, b } = container;
     * @param dependencyName
     */
    get<Name extends keyof ContainerResolvers>(dependencyName: Name): ContainerResolvers[Name];
    /**
     * Extends container with given function. It will pass container as an argument to the function.
     * Function should return new container with extended resolvers.
     * It is useful when you want to split your container into multiple files.
     * You can create a file with resolvers and extend container with it.
     * You can also use it to create multiple containers with different resolvers.
     *
     * For example:
     *
     *     const container = new DIContainer()
     *       .extend(addValidators)
     *
     *     export type DIWithValidators = ReturnType<typeof addValidators>;
     *     export const addValidators = (container: DIWithDataAccessors) => {
     *       return container
     *         .add('myValidatorA', ({ a, b, c }) => new MyValidatorA(a, b, c))
     *         .add('myValidatorB', ({ a, b, c }) => new MyValidatorB(a, b, c));
     *     };
     *
     * @param f
     */
    extend<E extends (container: IDIContainer<ContainerResolvers>) => any>(f: E): ReturnType<E>;
    private setValue;
    private toContainer;
}
