Open
Description
Description
This task aims at rethinking and rewriting how container inheritance and dependency lookup works between containers. This task should include rework or implementation of the following features
- allowing to set services globally, for a specific scope or as transient (we already support these)
- allow easily overwriting some of the dependencies of a given class (ref feature: transitional dependencyย #186)
- either making it easy to create a container with parent lookup (preferred)
- or allowing specifying custom overrides when calling
container.get()
- allow containers to lookup missing dependencies in their parents
- allow containers to inherit dependency metadata from their parents
- allow containers to dispose of themselves, destroying all locally registered service
Currently a container can be requested with containerInstance.of(containerId)
function. This returns a new container if no container exists with the given ID or returns the existing one. There are several problems with this solution:
- caller has no idea if the container existed before or not
- there is no way to specify how a container should inherit services from a parent container
- currently, there is no lookup in parent containers besides checking the default container for
global
services
Proposed solution
The new implementation should:
- allow specifying whether an error should be raised when a container exists with given ID
- allow parent-child relations, where a dependency is looked up in the parent container if it's doesn't exist in the child
- this should not be limited to a single level, but should recursively look up, until the dependency is found or a container specifies a lookup strategy that doesn't allow further lookups in the parent container
- allow cloning the registered metadata from the parent container
- either with values, meaning the resolved value of
ExampleClass
service will be the same for both containers- technical note: this means the metadata is shared and we need to store which container uses which
- technical note: what happens if one container resets it's value?
- or with definition only, meaning a different instance will be created for the child container
- technical note: how inherit value only dependencies?
- either with values, meaning the resolved value of
Proposed API
// default container instance, always exists, cannot be destroyed
import { Container } from 'typedi';
Container.of('my-new-container', {
/**
* What should happen if an existing Container is found with the given ID.
* - `throw` means TypeDI will raise an error
* - `overwrite` - the previous container is disposed and a new container is created
* - `returnExisting` - returns the existing container if the same parameters where specified
*/
onConflict: 'throw' | 'overwrite' | 'returnExisting',
/**
* Controls what should happen when a type is requested what doesn't exists in the current container.
* - 'allow' means the parent container can be checked for the given type, if found the parent container will hold the instance
* - 'localOnly' - means types are checked locally only and if no type found a ServiceNotFoundError is thrown
*/
lookupStrategy: 'allow' | 'localOnly',
/**
* Enables or disables the lookup for global services. By default every requested type is first checked if it's
* a global (singleton) service in the default container. This check bypasses the lookup strategy, so if this behavior
* is not desired it can be disabled via this flag.
*/
allowGlobalLookup: boolean;
/**
* Controls how the child container inherits the service definitions from it's parent
* - `none` means no metadata is inherited
* - `definitionOnly` means metadata is inherited and if an instance already exists it's ignored
* - `definitionWithValues` means metadata and service instance both inherited, when the parent container is disposed
* the instance in this container is preserved
*/
inheritanceStrategy: 'none' | 'definitionOnly' | 'definitionWithValues',
}