A zero-dependency DI container using TypeScript 5 standard decorators. No experimentalDecorators, no emitDecoratorMetadata, no reflect-metadata.
bun add @fxfn/injectimport { container } from "@fxfn/inject";
// Interface → implementation
container.register(IUserService, UserService);
const svc = container.resolve(IUserService); // new instance each time
// Singleton
container.register(IRouter, RouterService, { singleton: true });
const router = container.resolve(IRouter); // same instance every time
// Symbol token with a plain value
const ApiKey = Symbol("ApiKey");
container.register(ApiKey, process.env.API_KEY!);@inject and @injectall use the TC39 standard accessor keyword for lazy resolution.
import { inject, injectall } from "@fxfn/inject";
class MyService {
@inject(IConfig)
accessor config!: IConfig;
@injectall(IRoute)
accessor routes!: IRoute[];
}Dependencies are resolved on first property access, not at construction time.
Distinguish between multiple registrations of the same token:
container.register(IDatabase, PrimaryDb, { singleton: true, tag: "primary" });
container.register(IDatabase, ReplicaDb, { singleton: true, tag: "replica" });
class MyService {
@inject(IDatabase, { tag: "primary" })
accessor db!: IDatabase;
}When resolving without a tag, the first untagged registration wins. If none is untagged, the first registration is used as a fallback.
| Import | What it provides |
|---|---|
@fxfn/inject |
container, Container, inject, injectall, IContainer, Token, Constructor, RegistrationOptions, ResolveContext |
| Topic | Description |
|---|---|
| Container | register, resolve, resolveAll, singleton and transient lifetimes |
| Decorators | @inject, @injectall, tagged injection, lazy resolution |