inversify / InversifyJS

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

concrete injections without @inject not working #1500

Closed unknown1337 closed 1 year ago

unknown1337 commented 1 year ago

When using concrete constructor injection without @inject I get the error uncaught (in promise) Error: Missing required @inject or @multiInject annotation in: argument 0 in class SrvProfile.

Question, how to make concrete constructor injection working without @inject? THANKS

inv.app.ts

const appContainer = new Container({ autoBindInjectable: true });
appContainer.bind<SrvAuth>(SrvAuth).toSelf();
appContainer.bind<SrvHandleResp>(SrvHandleResp).toSelf();
appContainer.bind<SrvProfile>(SrvProfile).toSelf();
export { appContainer };

srv_profile.ts -not working-


@injectable()
export class SrvProfile { 
  constructor(private _srvAuth: SrvAuth) {  }
......
}

srv_profile.ts -IS working/ OK-


@injectable()
export class SrvProfile { 
  constructor(@inject(SrvAuth) _srvAuth: SrvAuth) {
    this._ss = _srvAuth;
  }
......
}

srvAuth.ts

@injectable()
export class SrvAuth {
....
}

Expected Behavior

injection working without adding @inject

Current Behavior

error: uncaught (in promise) Error: Missing required @inject or @multiInject annotation in: argument 0 in class SrvProfile.

Possible Solution

???

Your Environment

  1. windows
  2. quasar + vue3
  3. ts.config:

    {
    "extends": "@quasar/app-vite/tsconfig-preset",
    "compilerOptions": {
    "baseUrl": ".",
    "useDefineForClassFields": false, // needed for inversify lazy load to work https://www.typescriptlang.org/tsconfig#useDefineForClassFields & https://github.com/inversify/InversifyJS/issues/1212  & https://github.com/mobxjs/mobx/discussions/3216\
    
    "target": "ES6",
    "lib": ["es6", "dom"],
    "types": ["reflect-metadata"],
    "module": "commonjs",
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
    }
    }

Stack trace

Uncaught (in promise) Error: Missing required @inject or @multiInject annotation in: argument 0 in class SrvProfile.
    at getConstructorArgsAsTarget (reflection_utils.ts:98:13)
    at getConstructorArgsAsTargets (reflection_utils.ts:121:20)
    at getTargets (reflection_utils.ts:45:30)
    at getDependencies (reflection_utils.ts:14:10)
    at planner.ts:179:28
    at Array.forEach (<anonymous>)
    at _createSubRequests (planner.ts:164:18)
    at plan (planner.ts:240:5)
    at container.ts:623:21
    at Container2._get (container.ts:574:32)
..........
notaphplover commented 1 year ago

In theory, your code should work. emitDecoratorMetadata tsc option should be emitting class metadata and it should be propagated so inversify can instantiate your class. Can you make sure your compiled code has this emitted metadata? Some bundlers are a bit tricky in these regards. A repo with a minimum case would be great to have a look at the issue

unknown1337 commented 1 year ago

Thanks! How can I make sure the metadata is present? ;)

notaphplover commented 1 year ago

According to the docs:

 function LogMethod(
  target: any,
  propertyKey: string | symbol,
  descriptor: PropertyDescriptor
) {
  console.log(target);
  console.log(propertyKey);
  console.log(descriptor);
}

class Demo {
  @LogMethod
  public foo(bar: number) {
    // do nothing
  }
}

const demo = new Demo();

With emitDecoratorMetadata set to true the emitted JavaScript is:

"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
   if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
function LogMethod(target, propertyKey, descriptor) {
    console.log(target);
    console.log(propertyKey);
    console.log(descriptor);
}
class Demo {
    foo(bar) {
        // do nothing
    }
}
__decorate([
    LogMethod,
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Number]),
    __metadata("design:returntype", void 0)
], Demo.prototype, "foo", null);
const demo = new Demo();

The internals of inversify rely on these design:paramtypes metadata, so you should make sure your compiled code has those design:paramtypes metadata calls

unknown1337 commented 1 year ago

Thanks, that really helped (as its new to me). It seems that the design params are missing if I interpret correctly?

What could cause the design:paramtypes to not being generated (quasar 2 -> quasar dev). I (on purpose) made a mistake in TSconfig.json resulting in a error, thus the tsconfig file is being read .. =O

SRV_profile.'ts' when inspected using chrome devtools -> source -> srv_profile.ts

var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __decorateClass = (decorators, target, key, kind) => {
  var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target;
  for (var i = decorators.length - 1, decorator; i >= 0; i--)
    if (decorator = decorators[i])
      result = (kind ? decorator(target, key, result) : decorator(result)) || result;
  if (kind && result)
    __defProp(target, key, result);
  return result;
};
import { injectable } from "/node_modules/.q-cache/vite/spa/deps/inversify.js?v=a63f6374";
export let SrvProfile = class {
  constructor(_srvAuth) {
    this._srvAuth = _srvAuth;
  }
  getProfile() {
    debugger;
    return " 3";
  }
};
SrvProfile = __decorateClass([
  injectable()
], SrvProfile);

//# sourceMappingURL=data:application/json;base64,eyJ2ZX...............
notaphplover commented 1 year ago

Thanks, that really helped (as its new to me). It seems that the design params are missing if I interpret correctly?

You're very welcome. I wish I could be more helpful. Exactly, class metadata is not emitted.

What could cause the design:paramtypes to not being generated (quasar 2 -> quasar dev). I (on purpose) made a mistake in TSconfig.json resulting in a error, thus the tsconfig file is being read .. =O

I'm afraid that's out of my knowledge. If I had to guess, I would say your bundler is probably not supporting emitDecoratorMetadata option. Could you have a look at this issue? It seems you are not the only one having the same issue.

I would hazard to say this is not an issue related to the library. Should we proceed to close it?

unknown1337 commented 1 year ago

hmm ok, thanks a lot though! Feel free to close the issue;)

notaphplover commented 1 year ago

You're very welcome @unknown1337 :)