Injection decorators not working for Singletons with Interfaces
Describe the bug
Not sure whether this is a bug or I'm missing something, but surely it is a weird behavior (at least coming from other DI frameworks like Swift's Resolver).
I can't find a way to register two Singletons and inject the first one into the second one. I did nothing different than what I see in the Readme's Interface example, except that I need those implementations to be Singletons, thus registered with registerSingleton (which, by the way, I don't see documented in the Readme).
I also made an attempt with the following, with no luck:
container.register<Services.LoggerService>(
LoggerServiceName,
{
useClass: Adapters.LogAdapter,
},
{ lifecycle: Lifecycle.Singleton },
);
Everything works perfectly using the exact same code but resolving without decorators:
export class RouterAdapter implements RouterService<T1, T2> {
constructor() {
this.logger = container.resolve<LoggerService>(LoggerServiceName);
}
[...]
}
To Reproduce
Setup a ReactNative project with Typescript, following the official ReactNative doc. Also, follow the Tsyringe Getting Started to add required dependencies and TS configs (reflect-metadata, etc, etc). Then try the following:
//////////// index.js
import "reflect-metadata";
import "./ioc/setup.ts";
//////////// /ioc/setup.ts
import { container } from "tsyringe";
import { LoggerServiceName, RouterServiceName } from "./registry.ts";
import * as Services from "@ports"; // index for all services/ports
import * as Adapters from "@adapters"; // index for all adapters
import { T1, T2 } from "my-navigation-library";
container.registerSingleton<Services.LoggerService>(
LoggerServiceName,
Adapters.LoggerAdapter,
);
container.registerSingleton<Services.RouterService<T1, T2>>(
RouterServiceName,
Adapters.RouterAdapter,
);
//////////// ioc/registry.ts
export const LoggerServiceName = "LoggerService";
export const RouterServiceName = "RouterService";
//////////// adapters/router.adapter.ts
import { LoggerService, RouterService } from "@ports";
import { LoggerServiceName } from "@registry";
import { T1, T2 } from "my-navigation-library";
@injectable() // I also did an attempt with @authInjectable(), no luck
export class RouterAdapter implements RouterService<T1, T2> {
constructor(@inject(LoggerServiceName) private logger?: LoggerService) {}
[...]
}
//////////// adapters/logger.adapter.ts
import { LoggerService } from "@ports";
export class LoggerAdapter implements LoggerService {
[...]
}
//////////// app/firstScreen.js
import { container } from "tsyringe";
import { RouterServiceName } from "@registry";
someFunction() {
const router = container.resolve(RouterServiceName);
// throws the following
// Cannot inject the dependency at position #0 of "RouterAdapter" constructor. Reason: Attempted to construct an undefined constructor. Could mean a circular dependency problem. Try using `delay` function.
}
Expected behavior
- RouterAdapter gets instantiated upon first
resolve()call, and that instance should be "cached" in the container - LoggerAdapter gets instantiated upon first
resolve()call, aand that instance should be "cached" in the container - If the first
resolve()call happens to be the injection into a class constructor, it should work
Version: 4.6.0
Im in a very similar situation. React Native, TypeScript and trying to inject a singleton interface implementation by constructor, but is not found
Looks like prevention from falling to Service Locator anti-pattern ;)
Can't seem to get this working even the README way...
If we have such a decorator:
export function Decorator<T>(): (target: constructor<T>) => any {
return (target: constructor<T>): void => {
singleton(target);
I woul've expected something like this to work:
export function Decorator<T>(): (target: constructor<T>) => any {
return (target: constructor<T>): void => {
singleton(delay(() => target));
any workarounds?
Was able to come up with.
export function delayInjection<T>(target: constructor<T>): T {
return delay(() => target).createProxy((ctor) => globalContainer.resolve(ctor));
}
class A {
private service: Service = delayInjection(Service);