@elumixor/di
v0.3.2
Published
Very simple and lightweight Dependency Injection library for TypeScript
Readme
@elumixor/di
Minimal dependency injection for TypeScript. ~76 lines, zero dependencies, singleton-scoped global container.
Installation
npm install @elumixor/diQuick Start
import { di } from "@elumixor/di";
// 1. Mark a class as injectable
const UserService = di.injectable(
class UserService {
getUser(id: string) {
return { id, name: "Alice" };
}
},
);
// 2. Instantiate it (automatically registered in the container)
new UserService();
// 3. Retrieve it anywhere
const userService = di.inject(UserService);
userService.getUser("1"); // { id: "1", name: "Alice" }API
All functions are accessed via the di namespace object.
di.injectable(Class)
Wraps a class so that instantiating it automatically registers the instance in the global container.
const Logger = di.injectable(
class Logger {
log(msg: string) {
console.log(msg);
}
},
);
new Logger(); // registered automaticallyEach injectable class is a singleton — constructing it a second time throws an error.
di.inject(Class, options?)
Retrieves a registered instance from the container.
const logger = di.inject(Logger); // throws if not registeredPass { optional: true } to return undefined instead of throwing:
const logger = di.inject(Logger, { optional: true }); // Logger | undefineddi.provide(Class, instance)
Manually registers an instance. Useful for classes you don't control or for providing mock implementations in tests.
class ExternalApi {
fetch(url: string) {
/* ... */
}
}
const api = new ExternalApi();
di.provide(ExternalApi, api);
// later
di.inject(ExternalApi); // returns apiThrows if the class is already registered.
di.uninject(Class)
Removes a class from the container. Useful for cleanup in tests.
di.uninject(Logger);
// Logger can now be re-registeredPatterns
Abstract base class as a token
You can register against an abstract class, allowing consumers to depend on an abstraction:
abstract class Database {
abstract query(sql: string): unknown;
}
class PostgresDatabase extends Database {
query(sql: string) {
/* ... */
}
}
di.provide(Database, new PostgresDatabase());
// Consumers only know about Database
const db = di.inject(Database);Testing with mocks
// Reset and provide a mock before each test
di.uninject(UserService);
di.provide(UserService, { getUser: () => ({ id: "1", name: "Mock" }) } as any);Design
- Singleton scope — one instance per class, stored in a global
MaponglobalThis - Class constructors as tokens — no string keys, no symbols, no interfaces
- No auto-wiring — dependencies are resolved explicitly via
di.inject() - No decorators —
di.injectable()is a plain function wrapper, no decorator metadata required
