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

Missing required @injectable annotation in ThirdPartyClass #297

Closed
lukas-zech-software opened this issue Jul 22, 2016 · 10 comments
Closed

Comments

@lukas-zech-software
Copy link
Contributor

lukas-zech-software commented Jul 22, 2016

I have a class MyStream that extends the NodeJS.PassThrough stream class.
This class should be injected as dependency in another class called Consumer

Consumer and MyStream are both marked with @injectable and bound to the kernel

import { Kernel, injectable, inject } from 'inversify';

import { PassThrough } from 'stream';

@injectable()
class MyStream extends PassThrough {
  constructor() {
    super();
  }
}

class Consumer {
  constructor(@inject('MyStream') private myStream: MyStream) {
  }
}

const kernel = new Kernel();

kernel.bind('MyStream').to(MyStream);
kernel.bind('Consumer').to(Consumer);

kernel.get('MyStream');

Trying to resolve MyStream fails with the message Missing required @injectable annotation in: PassThrough

As PassThrough is not my own code, I cannot put the decorator on it.

I read through the issues and documentation but could not find a solution to this problem.
Two mentions are similiar, yet I could not adept the solution from there:
#262: In this the user seems to have loaded two different node modules, both under his ownership. Both modules imported reflect-metadata causing the Reflect singleton to be overwritten. In my case, only my own library imports reflect-metadata, the other module being the NodeJS API obviously does not load Reflect itself (I use NodeJs 5.7.0, no Reflect implemented there)

Inheritance Doc: It is mentioned here, that there are problems when injecting dependencies in the base class, if the constructor and super constructor have different parameter count.
This also does not seems to apply to my situation. I tried the mentioned workaround B nonetheless and injected the dependency in a property instead of the constructor, which yielded the same Error.

So my question is:

How can I resolve a dependency that itself extends a third party type?

Your Environment

  • Version used: 2.0.0-rc.1
  • Environment name and version: node.js 5.7.0
  • Operating System and version: OSX 10.11.6
@remojansen
Copy link
Member

remojansen commented Jul 22, 2016

You can invoke the decorator using the decorate function:

import { decorate, injectable } from "inversify";
decorate(injectable(), ClassName)

Check out https://github.com/inversify/InversifyJS/blob/master/wiki/basic_js_example.md for more info.

@lukas-zech-software
Copy link
Contributor Author

Thanks for the hint.
I read most of the documentation but skipped the JS example as I use TypeScript.
Maybe the decorate function could also be mentioned in another part of the documentation.

This solution resolves my issue.

But I have a follow up question which is more about how to solve the problem with third party dependency in the most elegant way.

Assuming my class from above needs to call the super constructor of the third party class with a parameter which is hard coded. My class itself does not need any parameter, therefore I have different counts of parameters and trigger the The number of constructor arguments in a derived class must be >= than the number of constructor arguments of its base class. error.

I cannot use any of the work arounds mention in the inheritance article, as all of these need to modify the base class. In my case its a NodeJS Stream wich must get the config object in the constructor.

My current solution to this problem is, to inject the config for the base class into my own class and then pass it to the base class, which means that I need to register the config in my kernel, which would basically not be necessary as it could be hard coded.
Another solution I could think of would be to inject a meaningless dummy parameter into the class just to increase the number of parameters.

Both solutions work, but seem far from perfect.
What approach would you suggest?

Solution 1:

decorate(injectable(), PassThrough);

@injectable()
class Dependency extends PassThrough {
  constructor(@inject('objectModeConfig') private config: any) {
    super(config);
  }
}

kernel.bind('Dependency').to(Dependency);
kernel.bind('objectModeConfig').toConstantValue({ objectMode: true });

Solution 2:

decorate(injectable(), PassThrough);

@injectable()
class Dependency extends PassThrough {
  constructor(@inject('dummy') private dummy: any) {
    super({ objectMode: true });
  }
}

kernel.bind('Dependency').to(Dependency);
kernel.bind('dummy').toConstantValue('dummy');

@remojansen
Copy link
Member

Maybe the decorate function could also be mentioned in another part of the documentation.

I will do something to improve this.

About your problem I think option 1 is better. I would recommend to use option one and inject the config as a constant value:

kernel.bind<any>("config").toConstantValue({ objectMode: true });

I will also try to think more about it and see If I add a better solution to the docs.

@remojansen
Copy link
Member

Hi @lukas-zech-software the next release will include a new workaround for your issue #333

@mrfoh
Copy link

mrfoh commented Mar 23, 2019

@remojansen will the decorate(injectable(), Classname); approach work for a class that has a constructor. In my use case, i have a class from another package.

export abstract class Repository<T> {
    constructor(name: string, schema: Schema) {
        // Do something
     }
}

and another class in another package using inversify/

@injectable()
export class BookRepository extends Repository<IBook> {
   constructor() {
       super('Book', BookSchema)l
     }
}

@kiran-bobade
Copy link

@mrfoh did you get the solution? I am facing the same issue.

@VictorMonte-zz
Copy link

@mrfoh me too :(
I'm trying to use MongType with Inversify.

@dustinlacewell
Copy link

Aww, I guess this issue was never solved before the project was abandoned :'(

@donnyroufs
Copy link

Aww, I guess this issue was never solved before the project was abandoned :'(

Poor us

@dirtyRuby
Copy link

Container option skipBaseClassChecks: true fixes this issue
Also use autoBindInjectable option for not to bind every single dependency

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

No branches or pull requests

8 participants