A lightweight dependency injection container for TypeScript and JavaScript
— compatible with both ECMAScript and TypeScript legacy decorators,
and no longer requires reflect-metadata.
Forked from Microsoft/tsyringe
TSyrinx is a modern rework and simplification of TSyringe with enhanced decorator support and a zero-reflect-metadata runtime.
- No
reflect-metadatarequired
(TSyrinx uses decorator context and type inference instead.) - Supports both decorators conventions
Works with ECMAScript decorators (Stage 3) and TypeScript legacy decorators. - Removed decorators
autoInjectable,injectAllWithTransform,injectAll,injectWithTransform, andinject(as decorators) were removed. - Simplified
@injectable
Only acceptsoptions: { token?: InjectionToken<T> | InjectionToken<T>[] }. - New runtime injection methods
Added functions:inject(token: InjectionToken<T>, options?)injectWithTransform(token, transform, ...args)injectAll(token, options?)injectAllWithTransform(token, transform, ...args)
Install via npm or pnpm:
npm install tsyrinx
# or
pnpm add tsyrinxUpdate your tsconfig.json to enable decorators (legacy or new):
{
"compilerOptions": {
"experimentalDecorators": true
}
}You do not need
"emitDecoratorMetadata": trueorreflect-metadata.
import { container, injectable } from "tsyrinx";
class Database {}
class Foo {
public db = inject(Database);
}
const foo = container.resolve(Foo);
console.log(foo.db instanceof Database); // ✅ trueOr using an explicit token:
@injectable({ token: "DB" })
class Database {}
class Foo {
db = inject<Database>("DB");
}
const foo = container.resolve(Foo);Register a class with one or more tokens.
@injectable({ token: ["FooService", "AnotherAlias"] })
class Foo {}Registers the class as a singleton within the global container.
@singleton()
class Foo {}Registers a class with a specific lifecycle (same as TSyringe):
Lifecycle.Transient(default)Lifecycle.SingletonLifecycle.ResolutionScopedLifecycle.ContainerScoped
@scoped(Lifecycle.ContainerScoped)
class ScopedFoo {}Instead of parameter decorators, TSyrinx exposes runtime injection functions to be called anywhere (constructor, method, factory, etc.):
import { inject, injectAll, injectWithTransform } from "tsyrinx";
class Foo {
db = inject(Database);
allRepos = injectAll("Repository");
config = injectWithTransform("Config", cfg => cfg.env);
}These helpers respect all container scopes and tokens.
The container API follows the same principles as TSyringe.
container.register(Foo, { useClass: Foo });
container.register("Config", { useValue: { env: "dev" } });const foo = container.resolve(Foo);
const config = container.resolve("Config");const allRepos = container.resolveAll("Repository");container.isRegistered(Foo); // trueconst child = container.createChildContainer();
child.register(Foo, { useClass: Foo });container.clearInstances();Circular dependencies are supported via the delay helper:
import { delay, inject } from "tsyrinx";
class Foo {
public bar = inject(delay(() => Bar));
}
class Bar {
public foo = inject(delay(() => Foo));
}All instances implementing Disposable are disposed automatically when calling:
await container.dispose();import { container, inject, injectable } from "tsyrinx";
class Database {
connect() {
console.log("DB connected");
}
}
class App {
db = inject(Database);
run() {
this.db.connect();
}
}
container.resolve(App).run();- Property injection decorators (e.g.
@injecton fields) - Metadata reflection (
reflect-metadata) - Legacy decorator metadata dependency
TSyrinx is a fork and rework of Microsoft/tsyringe. All credit to the original authors for their excellent foundation. TSyrinx aims to modernize and simplify dependency injection for the new decorators world.
Contributions are welcome! If you find a bug or want to suggest improvements, open an issue or PR.
MIT License Copyright © 2025 Ohad Cohen