Container
- Constructor
- Resolution
- Binding
- Inspection
- Lifecycle
- Ad-hoc Construction
- Validation
- Hierarchy
- Testing
- Properties
Constructor
new DiCaf(...modules: Module[])
new DiCaf(options: Options, ...modules: Module[])
Creates a new container and immediately applies every module passed to it.
When decorators is true (the default), autoWire() is called in the
constructor to pick up all @Injectable classes registered so far.
Options
| Option | Type | Default | Description |
|---|---|---|---|
profiles | Identifier[] | [] | Active profiles. Bindings annotated with @Profile are included only when their profile is in this list. |
defaultScopeId | Identifier | Scopes.SINGLETON | Scope used for bindings that do not specify one. |
parent | Container | — | Parent container. Unresolved keys are looked up in the parent. |
lazy | boolean | false | When true, singletons are not instantiated during init() — they are created on first access. |
metadataReader | MetadataReader | — | Custom reader for overriding binding defaults from external metadata. |
checks.scopes | ScopeCheckMode | 'compatible-scopes-only' | Scope compatibility validation mode. |
checks.circularReferences | boolean | true | Detect circular dependencies during init(). |
decorators | boolean | true | When true, calls autoWire() automatically in the constructor. |
ScopeCheckMode values:
'compatible-scopes-only'— a longer-lived binding cannot directly depend on a shorter-lived one.'no-mix'— all bindings in a dependency chain must share the same scope.'off'— no scope validation.
Resolution
get
get<T>(key: Key<T>): T
Resolves the binding for key and returns the instance.
Throws ErrNoResolutionForKey if no binding is registered for key.
Throws ErrNoUniqueInjectionForKey if multiple bindings exist for key and
none is marked @Primary.
const svc = di.get(UserService)
const logger = di.get<Logger>(Symbol.for('logger'))
getOptional
getOptional<T>(key: Key<T>): T | undefined
Like get() but returns undefined instead of throwing when the key is not
found.
const cache = di.getOptional(CacheService) // undefined if not registered
getMany
getMany<T>(key: Key<T>): T[]
Returns all instances bound to key. Throws ErrNoResolutionForKey if no
bindings exist.
const plugins = di.getMany<Plugin>(kPlugin)
wrap
wrap<T>(key: Key<T>): Provider<T>
Returns a Provider<T> that lazily resolves key on every call to
provider.get(). Useful for injecting a longer-lived dependency on a
shorter-lived one without scope violation.
const provider = di.wrap(HeavyService)
const instance = provider.get() // resolved lazily each time
wrapMany
wrapMany<T>(key: Key<T>): Provider<T[]>
Like wrap() but resolves all bindings for key on each provider.get().
Binding
bind
bind<T>(key: Key<T>): Binder<T>
Opens a new binding for key and returns a Binder to configure it. See the
Binder reference for the full fluent API.
di.bind(UserService).toSelf()
di.bind(Logger).toClass(ConsoleLogger)
di.bind('version').toValue('1.0.0')
rebind
rebind<T>(key: Key<T>): Binder<T>
Removes any existing binding for key, then opens a new binding. The
resulting Binder is identical to the one returned by bind().
di.rebind(Logger).toClass(StructuredLogger)
autoWire
autoWire(): void
Picks up all classes decorated with @Injectable that are registered in the
global decorator registry and adds them to this container. Called automatically
in the constructor when decorators: true.
Call it manually if you decorated classes are imported after the container was created.
Inspection
getBinding
getBinding<T>(key: Key<T>): Binding<T> | undefined
Returns the Binding descriptor for key, or undefined if not found.
getBindings
getBindings<T>(key: Key<T>): Binding<T>[]
Returns all Binding descriptors for key. Returns an empty array if none
exist.
getBindingsBy
getBindingsBy(predicate: (descriptor: BindingDescriptor) => boolean): BindingDescriptor[]
Returns all bindings for which predicate returns true.
const singletons = di.getBindingsBy(d => d.binding.scopeId === Scopes.SINGLETON)
getBindingsByLabel
getBindingsByLabel(label: symbol): BindingDescriptor[]
Returns all bindings whose labels array contains label.
const controllers = di.getBindingsByLabel(Symbol.for('controller'))
has
has<T>(key: Key<T>): boolean
Returns true if a binding is registered for key.
hasScopeInGraph
hasScopeInGraph(key: Key, scopeId: Identifier): boolean
Returns true if any binding in the transitive dependency graph of key uses
the given scope.
entries
entries(): IterableIterator<[Key, Binding]>
Returns an iterator over all [key, binding] pairs in the container. Use this
to feed buildBindingGraph().
size
readonly size: number
The number of bindings registered in the container.
Lifecycle
init
init(): Promise<void>
Initializes the container: validates the dependency graph, compiles injection resolvers, and eagerly instantiates non-lazy singletons.
Must be called before any resolution. Calling get() before init() may
return undefined or throw.
const di = new DiCaf(appModule)
await di.init()
dispose
dispose(): Promise<void>
Destroys the container. Runs @PreDestroy hooks on all singleton instances in
reverse initialization order.
resetInstances
resetInstances(): Promise<void>
Resets all instances. On the next resolution, fresh instances are created.
Note that async bindings are automatically reloaded after being disposed.
resetInstance
resetInstance(key: Key): Promise<void>
Resets the bindings associated with the given key.
Note that async bindings are automatically reloaded after being disposed.
Ad-hoc Construction
build
build<T>(ctor: Ctor<T>, injections?: Injection[]): T
Constructs a class instance outside of the container's binding registry. The
injections list is resolved from the container. Useful for constructing
request-level objects without registering them.
const handler = di.build(RequestHandler, [RequestContext])
builder
builder<T>(ctor: Ctor<T>, injections?: Injection[]): () => T
Returns a compiled factory function that creates instances of ctor using
dependencies from the container. Faster than calling build() in a hot path.
Validation
assertResolvable
assertResolvable(): void
Verifies that every binding in the container can be resolved: all required dependencies exist, and there are no missing keys. Throws descriptively on the first violation found.
Call this after init() as a startup health check:
await di.init()
di.assertResolvable()
Hierarchy
newChild
newChild(): Container
Creates a child container that inherits all bindings from the parent. The child can register additional bindings or override existing ones without affecting the parent.
Child containers must also be initialized with await child.init().
const parent = new DiCaf(sharedModule)
await parent.init()
const child = parent.newChild()
child.bind(TenantConfig).toValue(config)
await child.init()
parent
readonly parent?: Container
The parent container, if this is a child container.
Testing
snapshot
snapshot(): Snapshot
Captures the current set of bindings as a Snapshot. Does not include
instance state.
restore
restore(snap: Snapshot): void
Replaces the container's bindings with those from snap. All existing
instances are discarded.
Properties
| Property | Type | Description |
|---|---|---|
ready | boolean | true after init() completes. |
size | number | Number of bindings registered. |
profiles | ReadonlySet<Identifier> | Active profiles. |
parent | Container | undefined | Parent container. |
hooks | HookListener | Container lifecycle event emitter. See Hooks. |
postProcessors | Set<PostProcessor> | Post-init hooks run on every instance. |
refresher | Refresher | Controls REFRESH scope resets. |
requestScopeManager | RequestScopeManager | Controls REQUEST scope contexts. |