Two options of buildServiceProvider find wiring mistakes early. Both are off by default, like in .NET, where
ValidateScopes and ValidateOnBuild are switched on in development.
Builds the provider. Registrations are copied, so changing this collection afterwards
does not affect providers already built.
buildServiceProvider({
BuildOptions.validateScopes?: boolean |undefined
Throw a ScopeError when a scoped service is resolved from the root provider
or captured by a singleton. Recommended in development.
validateScopes: true, // enforce lifetime rules while resolving
BuildOptions.validateOnBuild?: boolean |undefined
Check every registration up front (missing dependencies, captive scoped services) and throw a ValidationError.
validateOnBuild: true, // check every registration now
});
Option
Checks
When
Throws
validateScopes
Scoped resolved from the root; scoped captured by a singleton
On every resolution
ScopeError
validateOnBuild
Every dependency is registered
In buildServiceProvider()
ValidationError
both
The above, plus every singleton that would capture a scoped service
In buildServiceProvider()
ValidationError
Turn both on in development and in tests. In production they cost little: validateOnBuild runs once at startup, and
validateScopes adds a short walk of the current resolution chain.
A scoped service resolved from the root provider is cached on the root and lives until shutdown: in effect a
singleton, holding whatever per-request state it saw first.
A singleton that depends on a scoped service (directly or through transients) captures the first scope’s instance and
hands it to every later request. That is a captive dependency:
Resolves token, throwing ServiceNotFoundError if it is not registered.
getRequiredService(
classReportCache
ReportCache);
// ScopeError: Scoped service 'DbTransaction' cannot be injected into singleton 'ReportCache'
// (ReportCache → DbTransaction): the singleton would keep one scope's instance forever.
The message shows the path from the singleton to the scoped service, so captures through intermediate transients are
easy to trace. This check also covers dependencies resolved inside factories, because it runs
during resolution.
The fix is one of: make the consumer scoped, make the dependency a singleton (if it holds no per-request state),
or resolve the scoped service per call from a scope instead of injecting it.
Checks every registration (plain and keyed) when the provider is built and throws one ValidationError that lists
every problem, not just the first. error.errors holds them as an array of strings.
interface
interfaceMailer
Mailer {
Mailer.send(to: string): Promise<void>
send(
to: string
to:string):
interfacePromise<T>
Represents the completion of an asynchronous operation
Builds the provider. Registrations are copied, so changing this collection afterwards
does not affect providers already built.
buildServiceProvider({
BuildOptions.validateOnBuild?: boolean |undefined
Check every registration up front (missing dependencies, captive scoped services) and throw a ValidationError.
validateOnBuild: true,
BuildOptions.validateScopes?: boolean |undefined
Throw a ScopeError when a scoped service is resolved from the root provider
or captured by a singleton. Recommended in development.
validateScopes: true });
} catch (
var error:unknown
error) {
if (
var error:unknown
errorinstanceof
classValidationError
validateOnBuild found problems. Every problem is listed in
ValidationError.errors
.
ValidationError)
var console:Console
The console module provides a simple debugging console that is similar to the
JavaScript console mechanism provided by web browsers.
The module exports two specific components:
A Console class with methods such as console.log(), console.error() and console.warn() that can be used to write to any Node.js stream.
A global console instance configured to write to process.stdout and
process.stderr. The global console can be used without importing the node:console module.
Warning: The global console object's methods are neither consistently
synchronous like the browser APIs they resemble, nor are they consistently
asynchronous like all other Node.js streams. See the note on process I/O for
more information.
Example using the global console:
console.log('hello world');
// Prints: hello world, to stdout
console.log('hello %s', 'world');
// Prints: hello world, to stdout
console.error(newError('Whoops, something bad happened'));
// Prints error message and stack trace to stderr:
// Error: Whoops, something bad happened
// at [eval]:5:15
// at Script.runInThisContext (node:vm:132:18)
// at Object.runInThisContext (node:vm:309:38)
// at node:internal/process/execution:77:19
// at [eval]-wrapper:6:22
// at evalScript (node:internal/process/execution:76:60)
// at node:internal/main/eval_string:23:3
constname='Will Robinson';
console.warn(`Danger ${name}! Danger!`);
// Prints: Danger Will Robinson! Danger!, to stderr
Example using the Console class:
constout=getStreamSomehow();
consterr=getStreamSomehow();
constmyConsole=new console.Console(out, err);
myConsole.log('hello world');
// Prints: hello world, to out
myConsole.log('hello %s', 'world');
// Prints: hello world, to out
myConsole.error(newError('Whoops, something bad happened'));
// Prints: [Error: Whoops, something bad happened], to err
Prints to stdout with newline. Multiple arguments can be passed, with the
first used as the primary message and all additional used as substitution
values similar to printf(3)
(the arguments are all passed to util.format()).
The captive-dependency check runs at build time only when validateScopes is on as well. With validateOnBuild alone
you get the missing-dependency check.
The graph below runs the same analysis on an editable set of registrations: captive edges are marked from the
singleton down to the scoped service it would capture.
6registrations
0cycles
2captive
0missing
SI singletonSC scopedTR transient cycle captive dependencyClick a service to resolve it for real.