Inversifyjs: Missing required @injectable annotation in `ThirdPartyClass`

Created on 22 Jul 2016  路  9Comments  路  Source: inversify/InversifyJS

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

Most helpful comment

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.

All 9 comments

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.

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

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.

Hi @lukas-zech-software the next release will include a new workaround for your issue https://github.com/inversify/InversifyJS/pull/333

@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
     }
}

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

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

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

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

Poor us

Was this page helpful?
0 / 5 - 0 ratings