tsyringe Alternative for TypeScript Dependency Injection
ts-ioc-container is a fast, lightweight TypeScript dependency injection
container for teams that like the simplicity of decorator-based DI but need more
control over scopes, providers, tokens, lifecycle hooks, and custom injection
strategies.
Why Look Beyond tsyringe?
tsyringe is a good fit when an application mainly needs constructor injection
and singleton/transient lifetimes. ts-ioc-container is designed for projects
that also need request scopes, page scopes, provider pipelines, explicit tokens,
aliases, lazy dependencies, lifecycle hooks, and customization points around how
objects are created.
Feature Comparison
| Need | ts-ioc-container fit |
|---|---|
| Fast TypeScript dependency resolution | Lightweight runtime with executable benchmark coverage against tsyringe |
| Request or transaction scopes | Tagged child containers and scoped registration rules |
| Decorator-based injection | @register, @inject, @onConstruct, @onDispose, and metadata utilities |
| Explicit dependency tokens | Class, key, alias, function, constant, lazy, and grouped token shapes |
| Custom factories | Provider APIs for values, classes, factories, arguments, singletons, visibility, and decoration |
| Lifecycle cleanup | Disposable scopes and onDispose hooks |
| Framework integration | Recipes for Express.js, Fastify, Next.js, React, and SolidJS |
| Custom behavior | Pluggable injectors, provider pipes, registration pipes, and custom hooks |
Scoped Lifecycles
Scoped containers make it practical to isolate per-request, per-transaction, per-page, or per-widget state while sharing application-wide services from a parent container.
const app = new Container({ tags: ['application'] });
function handleRequest() {
const request = app.createScope({ tags: ['request'] });
try {
return request.resolve('RequestHandler').handle();
} finally {
request.dispose();
}
}const app = new Container({ tags: ['application'] });
function handleRequest() {
const request = app.createScope({ tags: ['request'] });
try {
return request.resolve('RequestHandler').handle();
} finally {
request.dispose();
}
}Custom Providers
Provider behavior is composable. You can cache instances, pass runtime arguments, decorate services, restrict visibility, expose aliases, or delay work until a dependency is actually needed.
Tokens and Aliases
The token API keeps dependency requests explicit and reusable. Use keys for interfaces, class tokens for constructors, aliases for alternate names, grouped aliases for plugin-like collections, and lazy tokens for deferred work.
Hooks and Disposal
Lifecycle hooks let services initialize after construction and clean up when a scope is disposed. That matters for request state, transactions, sockets, subscriptions, and any dependency that owns resources.
When tsyringe Is Enough
Choose tsyringe when your application only needs straightforward constructor
injection, a familiar decorator API, and a small set of lifetime options.
When ts-ioc-container Fits Better
Choose ts-ioc-container when dependency injection is part of the application
architecture: request isolation, framework integration, lifecycle cleanup,
provider customization, token composition, and testable product behavior all
matter.
Continue with the Product Capabilities map or jump into Dependency and Scope.