inversify / InversifyJS

A powerful and lightweight inversion of control container for JavaScript & Node.js apps powered by TypeScript.
http://inversify.io/
MIT License
11.16k stars 713 forks source link

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

Open christianblos opened 5 years ago

christianblos commented 5 years ago

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"
    ]
    }
  5. 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(KatanaType).to(Katana).inSingletonScope(); container.bind(ShurikenType).to(Shuriken).inSingletonScope(); container.bind(NinjaType).to(Ninja).inSingletonScope();

container.get(NinjaType);

6. run `yarn start`
7. 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 :)
Laazarus commented 5 years ago

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

Roustalski commented 5 years ago

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

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

projekt86 commented 5 years ago

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

mrdulin commented 5 years ago

same issue.

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

michalzubkowicz commented 4 years ago

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

hamstakilla commented 4 years ago

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

csr632 commented 4 years ago

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

albertocorrales commented 4 years ago

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

csr632 commented 4 years ago

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

albertocorrales commented 4 years ago

@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

kegi commented 4 years ago

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 ?

NicholasKuchiniski commented 4 years ago

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

cesalberca commented 4 years ago

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

johnico commented 4 years ago

same issue for me in react. cra

thomai-d commented 4 years ago

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/

Maaartinus commented 4 years ago

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

johnico commented 4 years ago

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)

adem commented 4 years ago

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.

uqee commented 4 years ago

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.

ptaferner commented 4 years ago

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...

Wintereise commented 3 years ago

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.

omril1 commented 3 years ago

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.

flolu commented 3 years ago

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

{
  "compilerOptions": {
    "emitDecoratorMetadata": true
  }
}
marcosdipaolo commented 3 years ago

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"

georgwittberger-the-nu-company commented 2 years ago

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:

Mcdavid95 commented 2 years ago

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

Cmrickels commented 1 year ago

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.