Skip to main content

@inject

Injects a dependency into a service constructor.

Signature

// Inject class (Dependency or Service)
function inject<T extends Dependency>(
ServiceClass: new (...args: any[]) => T
): ParameterDecorator;

// Inject by key (arbitrary data)
function inject<T>(
key: DependencyKey<T>
): ParameterDecorator;

Parameters

ParameterTypeDescription
keyInjectionKeyDependency class or DependencyKey<T>
type InjectionKey = Ctr<any> | DependencyKey<any>;

Description

The @inject decorator:

  1. Marks parameter — tells the DI container which dependency to inject
  2. Two key typesDependency class or string constant DependencyKey<T>
  3. Auto-resolves — when creating via di.createService() dependencies are substituted
  4. Type-safe — TypeScript checks type correspondence

Basic Usage

import { Service, inject, OperationService } from '@iiiristram/sagun';

class UserService extends Service {
constructor(
@inject(OperationService) os: OperationService,
) {
super(os);
}
}

With Multiple Dependencies

import { Service, inject, OperationService, Dependency } from '@iiiristram/sagun';

class Logger extends Dependency {
toString() { return 'Logger'; }
log(msg: string) { console.log(msg); }
}

class ApiClient extends Dependency {
toString() { return 'ApiClient'; }
fetch(url: string) { return fetch(url); }
}

class DataService extends Service {
constructor(
@inject(OperationService) os: OperationService,
@inject(Logger) private logger: Logger,
@inject(ApiClient) private api: ApiClient,
) {
super(os);
}

@operation
*fetchData() {
this.logger.log('Loading data...');
return yield* call([this.api, this.api.fetch], '/data');
}
}

Registration and Creation

import { useDI } from '@iiiristram/sagun';

function App() {
const di = useDI();

// Register dependencies
di.registerService(new Logger());
di.registerService(new ApiClient());

// Create service — dependencies are injected automatically
const dataService = di.createService(DataService);
di.registerService(dataService);
}

Parameter Order

OperationService must be the first parameter for classes extending Service:

class MyService extends Service {
constructor(
@inject(OperationService) os: OperationService, // First
@inject(Logger) private logger: Logger, // Others
@inject(ApiClient) private api: ApiClient,
) {
super(os); // Pass to super
}
}

Injection via DependencyKey

To inject arbitrary data (not classes), use DependencyKey<T>:

import { DependencyKey } from '@iiiristram/sagun';

// Define type and key
export type AppConfig = {
apiUrl: string;
debug: boolean;
};

export const CONFIG_KEY = 'APP_CONFIG' as DependencyKey<AppConfig>;

Register data by key:

function App() {
const di = useDI();

// Register data by key
const config: AppConfig = {
apiUrl: 'https://api.example.com',
debug: true,
};
di.registerDependency(CONFIG_KEY, config);

// Now services can inject config
const service = di.createService(MyService);
}

Use in service:

import { Service, inject, OperationService } from '@iiiristram/sagun';
import { CONFIG_KEY, AppConfig } from './config';

class MyService extends Service {
private config: AppConfig;

constructor(
@inject(OperationService) os: OperationService,
@inject(CONFIG_KEY) config: AppConfig,
) {
super(os);
this.config = config;
}

*fetchData() {
const url = `${this.config.apiUrl}/data`;
return yield* call(fetch, url);
}
}

Combining Dependency Types

class ComplexService extends Service {
constructor(
@inject(OperationService) os: OperationService,
// Inject service (class)
@inject(AuthService) private auth: AuthService,
// Inject Dependency (class)
@inject(Logger) private logger: Logger,
// Inject data (key)
@inject(CONFIG_KEY) private config: AppConfig,
) {
super(os);
}
}

See Also