Skip to content

Providers ​

A provider binds a token to the value it produces. The providers array on @Injectable is a registry of these bindings, resolved on demand when a constructor parameter requests a token via @Inject. Resolution is by token, so the order of entries is irrelevant.

A token is any stable key: a class, an abstract class, an InjectionToken, or a plain string / symbol. xInject supports four provider forms.

Bare class (self-provided) ​

A class registered with @Injectable is its own token. Requesting it with @Inject resolves it directly - no entry in providers is required, so this is the common case for class dependencies.

ts
@Injectable()
class UserService {
    constructor(@Inject(Database) public db: Database) {}
}

Listing a bare class in providers is shorthand for { provide: TheClass, useClass: TheClass }.

useClass ​

Bind a token to a different class - swapping or redirecting an implementation. The token resolves to useClass instead of itself; that class is resolved through inject, so it carries its own metadata and dependencies.

ts
@Injectable({ providers: [{ provide: Logger, useClass: FileLogger }] })
class ReportService {
    constructor(@Inject(Logger) public logger: Logger) {} // gets a FileLogger
}

useFactory ​

Produce the value by calling a function. Its return value becomes the dependency; the tokens listed in deps are resolved and passed to it as arguments, in order.

ts
{ provide: CACHE, useFactory: (config) => createCacheClient(config), deps: [ REDIS_CONFIG ] }

useValue ​

Bind a token to a fixed, pre-built value - a config object, constant, or ready-made instance. It is provided as-is, with no instantiation or resolution.

ts
{ provide: API_CONFIG, useValue: { apiKey: 'sk-xxx', baseUrl: 'https://api.example.com' } }

Putting it together ​

ts
import { Injectable, InjectionToken, ProvidersType } from '@remotex-labs/xinject';

const ENV = new InjectionToken<'development' | 'production'>('ENV');
const CACHE = new InjectionToken<CacheClient>('CACHE');

const providers: ProvidersType = [
    UserService,                                                     // bare class (self-provided)
    { provide: Logger, useClass: FileLogger },                       // useClass
    { provide: CACHE, useFactory: createCacheClient, deps: [ ENV ] },// useFactory
    { provide: ENV, useValue: 'development' as const }               // useValue
];

Factory dependencies ​

useFactory declares its own dependencies with deps, a list of tokens resolved from the same registry before the factory runs. This lets a factory compose other providers:

ts
const CONFIG = new InjectionToken<{ n: number }>('CONFIG');
const RESULT = new InjectionToken<number>('RESULT');

@Injectable({
    providers: [
        { provide: CONFIG, useValue: { n: 41 } },
        { provide: RESULT, useFactory: (config: { n: number }) => config.n + 1, deps: [ CONFIG ] }
    ]
})
class Service {
    constructor(@Inject(RESULT) public result: number) {} // 42
}

Circular dependencies ​

xInject tracks the resolution path and throws when it detects a cycle, rather than overflowing the stack. The error names the chain:

ts
// A depends on B, B depends on A
inject(A); // throws: Circular dependency detected: A -> B -> A

Detection carries no module-level state - the path is threaded through each resolution and copied at every hop, so independent branches (a diamond, where two dependencies share a leaf) are never mistaken for a cycle.

Mutual class cycles

Because a class named in @Inject(Other) must already be defined, two classes that inject each other cannot be written directly today - a forwardRef escape hatch is not yet provided. Cycles through useFactory deps are detected as shown above.

Type guards ​

isProviderUseClass, isProviderUseValue, and isProviderUseFactory narrow an unknown provider to its binding shape. inject uses them internally; they are exported for advanced composition.

ts
import { isProviderUseValue } from '@remotex-labs/xinject';

isProviderUseValue({ provide: ENV, useValue: 'development' }); // true

Released under the Mozilla Public License 2.0