InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

"Missing required @inject or @multiInject annotation" even though it's there

Open christianblos opened this issue 7 years ago • 27 comments

Hey, I have the problem that I can't get the injects working. It always says Missing required @inject or @multiInject annotation. Event though I add this annotation it still complains (I don't have a circular dependency either).

Expected Behavior

Injection should work.

Current Behavior

I'm trying to get inversify working together with the latest create-react-app. Unfortunately no inject is working like expected. I took a really small example to reproduce this issue:

Steps to Reproduce (for bugs)

  1. create-react-app test --typescript
  2. cd test
  3. yarn add inversify reflect-metadata
  4. update tsconfig.json:
{
  "compilerOptions": {
    "target": "es5",
    "allowJs": true,
    "skipLibCheck": false,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve",
    "lib": ["es6", "dom"],
    "types": ["reflect-metadata", "node"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  },
  "include": [
    "src"
  ]
}
  1. replace whole content of src/index.tsx:
import {Container, injectable, inject} from 'inversify';
import 'reflect-metadata';

let container: Container = new Container();

const KatanaType: symbol = Symbol();
const ShurikenType: symbol = Symbol();
const NinjaType: symbol = Symbol();

@injectable()
class Katana {
    public hit() {
        return 'cut!';
    }
}

@injectable()
class Shuriken {
    public throw() {
        return 'hit!';
    }
}

@injectable()
class Ninja {

    public constructor(
        @inject(KatanaType) private katana: Katana,
        @inject(ShurikenType) private shuriken: Shuriken,
    ) {
    }

    public fight() {
        return this.katana.hit();
    }

    public sneak() {
        return this.shuriken.throw();
    }

}

container.bind<Katana>(KatanaType).to(Katana).inSingletonScope();
container.bind<Shuriken>(ShurikenType).to(Shuriken).inSingletonScope();
container.bind<Ninja>(NinjaType).to(Ninja).inSingletonScope();

container.get(NinjaType);
  1. run yarn start
  2. See error "Error: Missing required @inject or @multiInject annotation in: argument 0 in class Ninja. ".

Environment

Node version: v10.11.0 create-react-app version: 2.1.1

package.json:

{
  "name": "test",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@types/jest": "23.3.9",
    "@types/node": "10.12.10",
    "@types/react": "16.7.7",
    "@types/react-dom": "16.0.10",
    "inversify": "^5.0.1",
    "react": "^16.6.3",
    "react-dom": "^16.6.3",
    "react-scripts": "2.1.1",
    "reflect-metadata": "^0.1.12",
    "typescript": "3.1.6"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": "react-app"
  },
  "browserslist": [
    ">0.2%",
    "not dead",
    "not ie <= 11",
    "not op_mini all"
  ]
}

I'm not sure if I have a wrong configuration somewhere or if this is a problem in create-react-app. I appreciate your help :)

christianblos avatar Nov 25 '18 17:11 christianblos

I am having the same problem with my test project, I cannot get the inject working with react. I am not getting any error when I run the application but simply I get the injected service is always undefined. Best Regards

Laazarus avatar Jan 02 '19 14:01 Laazarus

I created a demo repo of this for ease of reproducing.

https://github.com/Roustalski/broken-inversify

Roustalski avatar Jan 28 '19 21:01 Roustalski

@Roustalski try to remove constructor and use property injection. I create PR to your repo.

projekt86 avatar Jan 30 '19 19:01 projekt86

same issue.

Missing required @inject or @multiInject annotation in: argument 0 in class xxx

mrdulin avatar Jun 06 '19 11:06 mrdulin

I can confirm that issue, also that @projekt86 workaround works. Do you have any idea what is cause of this?

michalzubkowicz avatar Sep 26 '19 13:09 michalzubkowicz

if you using index.ts, and import multiple classes from it, get rid of it. helped me.

hamstakilla avatar Oct 13 '19 07:10 hamstakilla

maybe related to babel: https://github.com/inversify/InversifyJS/issues/1007

csr632 avatar Oct 13 '19 09:10 csr632

Do you know if there is a solution for this issue? I'm using CRA and @inject decorator works without constructors, as @projekt86 suggested, but I would like to be able to use constructors in my typescript classes, as I was doing before installing inversify

albertocorrales avatar Feb 01 '20 17:02 albertocorrales

@albertocorrales Use tsc or tsloader to transform ts into js if you can. If you must use babel, add this plugin. See this for more info.

csr632 avatar Feb 02 '20 04:02 csr632

@csr632, thanks for your answer. I would like to continue using babel, as we have some plugins there to build our bundle. However, I tried to configure the babel plugin and I'm still having the runtime error:

Error: Missing required @inject or @multiInject annotation in: argument 0 in class Ninja.

Here I forked @Roustalski 's repo to configure babel plugin. I'm not sure if I'm missing something or it is just not working: https://github.com/albertocorrales/broken-inversify

albertocorrales avatar Feb 02 '20 09:02 albertocorrales

Here is how ~~I fixed it~~ (almost fixed it) on my React (CRA) project :

I didn't wanted to eject so I added react-app-rewired (https://github.com/timarney/react-app-rewired) I added babel-plugin-parameter-decorator (https://github.com/WarnerHooh/babel-plugin-parameter-decorator)

Here is the steps to configure react :

  1. yarn add react-app-rewired customize-cra @babel/plugin-proposal-decorators babel-plugin-parameter-decorator
  2. On package.json (scripts), replace "react-scripts" by "react-app-rewired"
  3. Create "config-overrides.js" at project root

config-overrides.js :

const { override, addDecoratorsLegacy, addBabelPlugin } = require('customize-cra')

module.exports = override(
    addDecoratorsLegacy(),
    addBabelPlugin('babel-plugin-parameter-decorator'),
)

~~Now, everything is working as expected and I can @inject() in constructor.~~ Well... it's working only on dev react-app-rewired start but when I build it with react-app-rewired build, I have that error on the browser console : ReferenceError: inject is not defined

I feel I'm close to fix this, anyone have a clue ?

kegi avatar Mar 13 '20 15:03 kegi

Same issue here. I cannot get a class from a container if this class contains a constructor that receive an argument.

NicholasKuchiniski avatar Apr 01 '20 23:04 NicholasKuchiniski

This is still happening, any news on what might be the problem?

cesalberca avatar Apr 13 '20 16:04 cesalberca

same issue for me in react. cra

johnico avatar May 02 '20 19:05 johnico

EDIT: Ok, for more background information. Seems like this is not a bug. It's intentional by facebook: https://create-react-app.dev/docs/can-i-use-decorators/

thomai-d avatar May 03 '20 08:05 thomai-d

@thomai-d Not exactly. With CRA, it doesn't work as it doesn't support it. With react-app-rewired, you can get decorators working. They work for me with mobx. I tried pretty everything and didn't get babel-plugin-parameter-decorator working, so for me, inversify works only for parameter-less constructors. Even the case when the injected thing is a class itself, which need no decorator doesn't work.

Maaartinus avatar May 04 '20 07:05 Maaartinus


container.bind<IApi>(TYPES.Api).toDynamicValue(() => {
        const logger = 
 container.get<ILogger>(TYPES.Logger);
  const config = container.get<IConfig>(TYPES.ConfigService);
  return new Api(config, logger);
});

After this, I can inject in the constructor and not as property. Is it good? What is the price I pay here. I want si only for services and not in component because component does not know the service (redux)

johnico avatar May 05 '20 22:05 johnico

Strangely enough I only have this issue when running tests. I have the same setup as @kegi and it (mostly) works fine, even building the project works fine. However, running react-app-rewired test, which runs a test that calls container.get in beforeEach yields the same error Missing required @inject or @multiInject annotation in: argument 0 in class MyClass.. Anyone else dealing with the same issue?

EDIT: I have to take that back, I get a ReferenceError: inject is not defined in my production build. I've decided to stay away from customize-cra and react-app-rewired for now, it introduced too many unknown issues. I'll try to get constructor injection to work by ejecting - will let you know when I find out anything new.

adem avatar May 24 '20 14:05 adem

Fixed the issue by adding more babel plugins into the test environment, my babel.config.js now looks like:

module.exports = {
  env: {
    test: {
      plugins: [
        'transform-require-context',
+        'babel-plugin-transform-typescript-metadata',
+        'babel-plugin-parameter-decorator',
      ],
    },
  },
};

Hope this helps.

uqee avatar Jun 10 '20 16:06 uqee

if you using index.ts, and import multiple classes from it, get rid of it. helped me.

This solved the issue for me too, but I have no idea why...

ptaferner avatar Jun 19 '20 15:06 ptaferner

This seems to happen when you have a circular dependency issue, i.e: A requires B, B in turn requires (not via DI, just via an import) A.

https://github.com/aackerman/circular-dependency-plugin can be a handy way of chasing it down.

Wintereise avatar Oct 15 '20 03:10 Wintereise

The cause of the issue is most likely a circular dependency between modules, and that's why index file with all the services/stores/modules can sometimes cause it (it depends on all the services which sometimes depend on other services.

You can play with the order of the imports in the index and it might help, but it's not guaranteed. Webpack doesn't help detecting it out of the box, when a circular dependency happens webpack just gives you an undefined reference for the module at the top level import.

Here's a great blog post about it: https://medium.com/@catalin.luntraru/webpack-is-smart-enough-to-not-fall-for-circular-dependency-881578246aeb

In my use case I solved it by a dynamic import to the module I know I have a circular reference with.

omril1 avatar Jan 18 '21 15:01 omril1

My problem was that I've needed to add emitDecoratorMetadata to my tsconfig.json

{
  "compilerOptions": {
    "emitDecoratorMetadata": true
  }
}

flolu avatar Jan 29 '21 18:01 flolu

Could someone inject dependencies at the constructor of a Mobx store? because I could achieve it only with property injection. Ortherwise I also get the "Missing required @inject or @multiInject annotation"

marcosdipaolo avatar Aug 21 '21 18:08 marcosdipaolo

For those who want to use InversifyJS with Next.js - here is the configuration I had to adjust.

tsconfig.json (if you are using TypeScript)

Add the following properties to compilerOptions:

{
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "types": ["reflect-metadata"],
}

babel.config.js

Create Babel config file in project root directory and add this content:

module.exports = {
  presets: ['next/babel'],
  plugins: [
    'babel-plugin-transform-typescript-metadata',
    ['@babel/plugin-proposal-decorators', { legacy: true }],
  ],
}

Install the following NPM modules as dev dependencies:

  • babel-plugin-transform-typescript-metadata
  • @babel/plugin-proposal-decorators
  • @babel/core

For those who want to use InversifyJS with Next.js - here is the configuration I had to adjust.

tsconfig.json (if you are using TypeScript)

Add the following properties to compilerOptions:

{
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "types": ["reflect-metadata"],
}

babel.config.js

Create Babel config file in project root directory and add this content:

module.exports = {
  presets: ['next/babel'],
  plugins: [
    'babel-plugin-transform-typescript-metadata',
    ['@babel/plugin-proposal-decorators', { legacy: true }],
  ],
}

Install the following NPM modules as dev dependencies:

  • babel-plugin-transform-typescript-metadata
  • @babel/plugin-proposal-decorators
  • @babel/core

This fixed it for me. Thanks @georgwittberger-the-nu-company

Mcdavid95 avatar Jan 10 '22 12:01 Mcdavid95

This is 9/10 times due to a circular dependency. In my case it was Service1 injecting Service2 in its constructor and Service2 injecting Service1 in its constructor.

What a poor error message though.

Cmrickels avatar Mar 30 '23 20:03 Cmrickels