Decorators
Vercube provides a set of decorators for dependency injection. These decorators help you declare dependencies and configure how they are injected into your classes.
Available Decorators
@Inject
The @Inject
decorator is used to inject a required dependency into a class property.
typescript
class UserService {
@Inject(Logger)
private logger!: Logger;
@Inject(UserRepository)
private userRepository: UserRepository;
}
Parameters
key: ServiceKey<T>
- The key of the service to inject
Usage Notes
- Can only be used on class properties
- The service must be registered in the container
- Throws an error if the service is not found
@InjectOptional
The @InjectOptional
decorator is used to inject an optional dependency. If the service is not registered, the property will be undefined.
typescript
class UserService {
@InjectOptional(Logger)
private logger?: Logger;
@InjectOptional(UserRepository)
private userRepository?: UserRepository;
}
Parameters
key: ServiceKey<T>
- The key of the service to inject
Usage Notes
- Can only be used on class properties
- The service does not need to be registered
- The property type should be marked as optional with
?
@Init
The @Init
decorator marks a method to be called during initialization of a service. This is useful for setup tasks that need to be performed after all dependencies are injected.
typescript
class UserService {
@Inject(Logger)
private logger!: Logger;
@Init()
async initialize() {
await this.logger.info('UserService initialized');
// Perform initialization tasks
}
}
Usage Notes
- The decorated method will be called after all dependencies are injected
- Can be async
- Multiple
@Init
methods are executed in the order they are defined
Usage Notes
- When
autoBindInjectable
is enabled in the container options, decorated classes are automatically registered - Useful for reducing boilerplate in service registration
- Can be used with interfaces to specify the service type
Examples
Basic Dependency Injection
typescript
class UserController {
@Inject(UserService)
private userService: UserService;
@Inject(Logger)
private logger: Logger;
async getUser(id: string) {
this.logger.info(`Fetching user ${id}`);
return this.userService.getUser(id);
}
}
Optional Dependencies
typescript
class NotificationService {
@InjectOptional(EmailService)
private emailService?: EmailService;
@InjectOptional(SMSService)
private smsService?: SMSService;
async notify(user: User, message: string) {
if (this.emailService) {
await this.emailService.send(user.email, message);
}
if (this.smsService) {
await this.smsService.send(user.phone, message);
}
}
}
Initialization
typescript
class DatabaseService {
@Inject(Logger)
private logger!: Logger;
private connection?: Connection;
@Init()
async initialize() {
this.logger.info('Initializing database connection');
this.connection = await this.connect();
await this.runMigrations();
}
private async connect(): Promise<Connection> {
// Connection logic
}
private async runMigrations(): Promise<void> {
// Migration logic
}
}
Interface-based Injection
typescript
interface ILogger {
info(message: string): void;
error(message: string): void;
}
class ConsoleLogger implements ILogger {
info(message: string): void {
console.log(message);
}
error(message: string): void {
console.error(message);
}
}
class UserService {
@Inject(ILogger)
private logger!: ILogger;
}
Best Practices
Use Constructor Injection When Possible
- Makes dependencies explicit
- Easier to test
- Better TypeScript support
Use Property Injection for Optional Dependencies
- Clearer intent
- More flexible
- Better for circular dependencies
Use
@Init
for Complex Setup- Keep constructors simple
- Handle async initialization
- Perform setup tasks after injection
Handle Optional Dependencies Gracefully
- Check for existence before use
- Provide fallback behavior
- Document optional nature
See Also
- Container - The core container class and its methods
- Types - Type definitions used in the DI system
- Advanced Topics - Advanced usage patterns and techniques