💎 Value Objects
/** * @value */const config: IConfig = { apiUrl: 'https://api.example.com' };
/** * @value */const userService: IUserService = { getUser: (id: string) => Promise.resolve({ id, name: 'User' })};Write your classes, interfaces, value objects, and factory functions in pure TypeScript:
💎 Value Objects
/** * @value */const config: IConfig = { apiUrl: 'https://api.example.com' };
/** * @value */const userService: IUserService = { getUser: (id: string) => Promise.resolve({ id, name: 'User' })};🏭 Factory Functions (Separate Params)
/** * @factory */function createService(repo: IRepo1, config: IConfig) { return (userId: string) => { if (config.environment === 'prod') { return new ProductionService(repo, userId); } return new DevelopmentService(repo, userId); };}🏭 Factory Functions (Context Object)
/** * @factory */function createTodoUseCase( context: { userRepo: IUserRepository, todoRepo: ITodoRepository }) { return (userId: string, title: string): void => { const user = context.userRepo.getUser(userId); console.log(`Creating todo for ${user}`); context.todoRepo.saveTodo(title); };}🏗️ Classes
interface IService1 { getData(id: string): Promise<any>;}
interface IRepo1 { findById(id: string): Promise<any>;}
class Repo1 implements IRepo1 { async findById(id: string) { /* ... */ }}
class Service1 implements IService1 { constructor(private repo: IRepo1) {} async getData(id: string) { /* ... */ }}📐 Abstract Classes
abstract class BaseRepo { abstract findById(id: string): Promise<any>;}
class Repo1 extends BaseRepo { async findById(id: string) { /* ... */ }}🏗️ Modules
export class UserService implements IUserService { constructor(private userRepo: IUserRepository) {}}
// todo/TodoService.tsexport class TodoService implements ITodoService { constructor( private todoRepo: ITodoRepository, private userRepo: IUserRepository // Cross-module! ) {}}🔄 Lifecycle
// Singleton (default)export class SingletonService { constructor(private logger: ILogger) {}}
// Transient/** * @scope transient */export class TransientService { constructor(private logger: ILogger) {}}Run the CLI command to auto-generate, or type the container registration code manually:
npx @notjustcoders/ioc-arise generateIf you used the CLI, it analyzes your code and generates two files. If you typed it manually, you’ll have the same structure:
container.gen.d.ts - Type-safe registry interface:
// container.gen.d.ts (auto-generated)import type { IConfig } from './config';import type { IUserService } from './userService';import type { IService1 } from './services/IService1';import type { IRepo1 } from './repositories/IRepo1';import type { BaseRepo } from './repositories/BaseRepo';
export interface ContainerRegistry { 'IConfig': IConfig; 'IUserService': IUserService; 'IService1': IService1; 'IRepo1': IRepo1; 'BaseRepo': BaseRepo;}container.gen.ts - Container with all registrations:
// container.gen.ts (auto-generated)import { Container, Lifecycle } from '@notjustcoders/di-container';import type { ContainerRegistry } from './container.gen.d';import { config } from './config';import { userService } from './userService';import { createService } from './createService';import { Repo1, Service1 } from './classes';
export const container = new Container<ContainerRegistry>();
// All your value objects, factories, and classes are registered!container.register('IConfig', { useValue: config });container.register('IUserService', { useValue: userService });container.register('IService1', { useFactory: createService, dependencies: ['IRepo1', 'IConfig']});container.register('IService1', { useClass: Service1, dependencies: ['IRepo1'], lifecycle: Lifecycle.Singleton});// ... and moreimport { container } from './container.gen';
const service = container.resolve('IService1');// ^? IService1 - Full IntelliSense!npm install @notjustcoders/di-container