Skip to content

@Injectable

@Injectable registers a class so it can be resolved by inject. It records the class's scope, its providers, and an optional custom factory. A class that is never decorated cannot be injected - inject throws for it.

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

@Injectable()
class OrderService {}

Options

@Injectable(options?) accepts an InjectableOptionsInterface:

OptionTypeDefaultDescription
scope'singleton' | 'transient''transient'Instance lifetime. See Scopes.
providersProvidersType-Token registry resolved on demand when a parameter requests a token.
factory(...deps) => Instance-Replaces constructor instantiation. Receives the resolved arguments.
ts
const SQL = new InjectionToken<string>('SQL');

@Injectable({ scope: 'singleton', providers: [{ provide: SQL, useValue: 'SELECT 1' }] })
class Database {
    constructor(@Inject(SQL) private sql: string) {}
}

providers is a registry keyed by token, not an ordered argument list - entries are resolved only when a constructor parameter requests them through @Inject. See Providers for every provider form.

Custom factory

When a class needs creation logic beyond new, supply a factory. It receives the resolved constructor arguments in order and its return value becomes the instance.

ts
@Injectable({
    factory: (logger, metrics) => new MonitoredService(logger, metrics),
    providers: [{ provide: LOGGER, useClass: LoggerService }, { provide: METRICS, useClass: MetricsService }]
})
class PaymentGateway {
    constructor(@Inject(LOGGER) logger: Logger, @Inject(METRICS) metrics: Metrics) {}
}

Where metadata lives

@Injectable stores its options on the class itself (via a private symbol), not in a shared module-level map. This keeps the container free of global state - see No global container. Only the class's own registration is used, so a subclass is not treated as injectable through inheritance.

Reading the registration

Use getInjectableOptions to read back what a class was registered with (or undefined if it was never decorated). Useful for tooling and tests.

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

getInjectableOptions(Database);  // { scope: 'singleton', providers: [ ... ] }
getInjectableOptions(class {});  // undefined

Applying without decorator syntax

@Injectable is a plain function, so you can apply it directly when you are not using decorator syntax. Note that @Inject is a parameter decorator and does require decorator syntax on the constructor.

ts
class Service {}
Injectable({ scope: 'singleton' })(Service);

Released under the Mozilla Public License 2.0