Skip to content

Instance Factories

An instance factory is an exported function whose explicit return type annotation resolves to a registered interface. No @factory annotation is needed — the return type is the detection signal. The function is registered under the interface name as the token, exactly like a class that implements the interface.

Separate Parameters Pattern:

repositories/createUserRepository.ts
import { IAppConfig } from '../config/IAppConfig';
import { ILogger } from '../logger/ILogger';
import { IUserRepository } from './IUserRepository';
// The `: IUserRepository` return type is enough — no @factory needed.
export function createUserRepository(
config: IAppConfig,
logger: ILogger,
): IUserRepository {
if (config.getStorageType() === 'persistent') {
logger.info('Using persistent storage');
return new PersistentUserRepository(config.getDbPath());
}
logger.info('Using in-memory storage');
return new InMemoryUserRepository();
}

Context Object Pattern:

repositories/createUserRepository.ts
import { IAppConfig } from '../config/IAppConfig';
import { ILogger } from '../logger/ILogger';
import { IUserRepository } from './IUserRepository';
export function createUserRepository(
context: { config: IAppConfig; logger: ILogger },
): IUserRepository {
const { config, logger } = context;
if (config.getStorageType() === 'persistent') {
logger.info('Using persistent storage');
return new PersistentUserRepository(config.getDbPath());
}
logger.info('Using in-memory storage');
return new InMemoryUserRepository();
}

Promise<IInterface> and Awaited<Promise<IInterface>> return types are also detected.

Terminal window
npx @notjustcoders/ioc-arise generate

container.gen.d.ts:

import type { IUserRepository } from './repositories/IUserRepository';
import type { IAppConfig } from './config/IAppConfig';
import type { ILogger } from './logger/ILogger';
export interface ContainerRegistry {
'IUserRepository': IUserRepository;
'IAppConfig': IAppConfig;
'ILogger': ILogger;
}

container.gen.ts (separate params):

import { Container, Lifecycle } from '@notjustcoders/di-container';
import type { ContainerRegistry } from './container.gen.d';
import { createUserRepository } from './repositories/createUserRepository';
import { appConfig } from './config/appConfig';
import { consoleLogger } from './logger/consoleLogger';
export const container = new Container<ContainerRegistry>();
container.register('IUserRepository', {
useFactory: createUserRepository,
dependencies: ['IAppConfig', 'ILogger'],
lifecycle: Lifecycle.Singleton,
});
container.register('IAppConfig', { useValue: appConfig, lifecycle: Lifecycle.Singleton });
container.register('ILogger', { useValue: consoleLogger, lifecycle: Lifecycle.Singleton });

container.gen.ts (context object):

container.register('IUserRepository', {
useFactory: (config, logger) => createUserRepository({ config, logger }),
dependencies: ['IAppConfig', 'ILogger'],
lifecycle: Lifecycle.Singleton,
});
import { container } from './container.gen';
const repo = container.resolve('IUserRepository');
// ^? IUserRepository — full type safety
❌ Multiple implementation providers found for interface 'IUserRepository':
• class:InMemoryUserRepository
• factory:createUserRepository