Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Injection decorators not working for Singletons with Interfaces #189

Open
39otrebla opened this issue Jan 15, 2022 · 3 comments
Open

Injection decorators not working for Singletons with Interfaces #189

39otrebla opened this issue Jan 15, 2022 · 3 comments
Assignees

Comments

@39otrebla
Copy link

39otrebla commented Jan 15, 2022

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

  1. RouterAdapter gets instantiated upon first resolve() call, and that instance should be "cached" in the container
  2. LoggerAdapter gets instantiated upon first resolve() call, aand that instance should be "cached" in the container
  3. If the first resolve() call happens to be the injection into a class constructor, it should work

Version:
4.6.0

@carlossalasamper
Copy link

Im in a very similar situation. React Native, TypeScript and trying to inject a singleton interface implementation by constructor, but is not found

@ArtVolchara
Copy link

Looks like prevention from falling to Service Locator anti-pattern ;)

@leppaott
Copy link

leppaott commented Feb 13, 2023

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);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants