Skip to content

Lifetimes

InferDI has three lifetimes:

LifetimeCreatedCached onDisposed by container
singletononce per owning containerowner containeryes
scopedonce per child scopechild scopeyes
transientevery resolveneverno

Resolve scoped keys from a child returned by createScope(). The default checked contract rejects attempts to resolve them from the root container.

The Lifetime Rule

A singleton cannot directly depend on a scoped or transient service. A singleton is created once and shared across every request, so if it captures a scoped value — the current request's context, user, or transaction — that one request's state silently bleeds into all the others. InferDI makes that edge unrepresentable in the type system instead of leaving it to code review.

ts
import { 
Container
} from '@inferdi/inferdi'
class
RequestContext
{
readonly
requestId
= 'req-1'
} class
UserService
{
constructor(readonly
request
:
RequestContext
) {}
} new
Container
()
.
registerClass
('request',
RequestContext
, [], 'scoped')
.
registerClass
('users',
UserService
, ['request'], 'singleton')

TypeScript rejects that registration in every runtime contract. The default checked contract also rejects the same shape at runtime if a cast bypasses the type system.

Declared scope inputs count as scoped dependencies. Register a consumer that reads a request, auth context, tenant, or job payload as scoped or transient; the compiler rejects a singleton consumer before runtime. See Scope Inputs.

Runtime enforcement is controlled by the container contract. See Container Options for the exact behavior of the default and fast contracts.