InversifyJS icon indicating copy to clipboard operation
InversifyJS copied to clipboard

"Cannot read 'name' of undefined" happens occasionally when incorrect import order

Open shevchenkobn opened this issue 7 years ago • 7 comments

The issue is quite vague. I am creating the project (code). It is a back-end, so I have an entry point to my server and a cli script that populates the database. The problem is that my UserModel, that has a single dependency (a connection), can be container.get() from the CLI database script and fails when I try to start the server within the same project. All annotations are set, 'reflect-metadata' is imported (even tried doing it several times :) ) and I see no differences between server and CLI scripts (regarding Inversify).

Expected Behavior

The UserModel instance can be acquired under any circumstances (see code).

Current Behavior

When I try to import from server scripts, I get Cannot read 'name' of undefined error:

TypeError: Cannot read property 'name' of undefined
    at Object.getFunctionName (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/utils/serialization.js:94:11)
    at Object.getDependencies (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/reflection_utils.js:11:43)
    at /home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:106:51
    at Array.forEach (<anonymous>)
    at _createSubRequests (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:94:20)
    at Object.plan (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:136:9)
    at /home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:317:37
    at Container._get (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:310:44)
    at Container.get (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:230:21)
    at Object.<anonymous> (/home/bogdan/nure/courseProject/DrOwn-server/dist/services/authentication.service.js:21:41)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Module.require (internal/modules/cjs/loader.js:636:17)

Possible Solution

None. I have tried all the pieces of advice from #584, #488 and #961 (the closest one, I think it is similar to mine).

Steps to Reproduce (for bugs)

The problem is I have tried to create minimalistic example that shows this error, but every time I tried to do this (outside and inside) the project I failed. In fact, it works even within current project (from the CLI database script). So my example is this project (sorry to bother you with >3000 dependencies :) ). Steps to start the project:

  1. git clone https://github.com/shevchenkobn/DrOwn-server.git
  2. git checkout f676ae87b13a8372e00401e2cd1e5bc04810032b
  3. npm i
  4. npm start (the JS code is built and up-to-date; if you want to rebuild it run npm run build. In this case you would probably install devDependencies by npm i -D and npm run npm:g:dev or npm run npm:g:dev:win - global packages for development)
  5. See the error.

Context

Possibly, the issue is connected with sideeffects of either Inversify with reflect-metadata or some other packages and may be relevant to other developers. Again, the issue is similar to #961

I really liked your package but, unfortunately, I have to use some alternative DI library. Hope the issues is resolved and I will get back to Inversify.

Your Environment

  • Version used: 5.0.1
  • Environment name and version (e.g. Chrome 39, node.js 5.4): Node.JS v10.13.0, TypeScript Version 3.2.0-dev.20181110
  • Operating System and version (desktop or mobile): Ubuntu 18.04.1 LTS
  • Link to your project: https://github.com/shevchenkobn/DrOwn-server/tree/f676ae87b13a8372e00401e2cd1e5bc04810032b

Stack trace

TypeError: Cannot read property 'name' of undefined
    at Object.getFunctionName (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/utils/serialization.js:94:11)
    at Object.getDependencies (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/reflection_utils.js:11:43)
    at /home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:106:51
    at Array.forEach (<anonymous>)
    at _createSubRequests (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:94:20)
    at Object.plan (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/planning/planner.js:136:9)
    at /home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:317:37
    at Container._get (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:310:44)
    at Container.get (/home/bogdan/nure/courseProject/DrOwn-server/node_modules/inversify/lib/container/container.js:230:21)
    at Object.<anonymous> (/home/bogdan/nure/courseProject/DrOwn-server/dist/services/authentication.service.js:21:41)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Module.require (internal/modules/cjs/loader.js:636:17)

shevchenkobn avatar Nov 25 '18 19:11 shevchenkobn

I have fixed this issue. If you have a complex project with lots of dependencies and you don't have to worry about the way they are resolved just add import './pass/to/your/di.container'; at the beginning of the file. di.container in this context is file where you create container and add all dependencies there. Example: https://github.com/shevchenkobn/NewCMMS-server/blob/master/src/di/container.ts

Explanation: tl;dr: check your import tree manually. Consider moving objects managed by your DI into separate files in a type-per-file manner. It may be issue with circular imports or similar.

Node modules that have your @injectables probably import their @inject types directly (not for instantiation). Imports are done at the beginning of the file when @injectable type is not even declared. So if your module with @injectable is imported in DI container module, it may get an undefined instead of your @injectable. That is what I had. Inversify tries to report the problem and fails.

Maybe a safeguard should be added to the library? It would be better to check if the type is undefined and report it before trying to read its name. Something like Error: The dependency Symbol.for(type) is not defined. Consider reordering imports or import your container in the entry-point file.

shevchenkobn avatar Nov 26 '18 04:11 shevchenkobn

@shevchenkobn I've run into this issue recently. Could you elaborate on what you mean by:

"If you have a complex project with lots of dependencies and you don't have to worry about the way they are resolved just add import './pass/to/your/di.container'; at the beginning of the file."?

jimador avatar Feb 04 '20 22:02 jimador

@shevchenkobn I've run into this issue recently. Could you elaborate on what you mean by:

"If you have a complex project with lots of dependencies and you don't have to worry about the way they are resolved just add import './pass/to/your/di.container'; at the beginning of the file."?

I have updated the previous post. Please, reread the passage you quoted and tl;dr section.

shevchenkobn avatar Feb 05 '20 09:02 shevchenkobn

@shevchenkobn I am sorry but after reading your answer multiple times and checking your code and jimador one, I still do not understand how to fix the issue.

Here is what I have :

// inversify.config.ts
import {TYPES} from "./Types";
import {Container} from "inversify";
import {Config} from "../models/Config";
import {ZoneService} from "../services/zones/ZoneService";
import {IZoneService} from "../services/interfaces/IZoneService";

const DI = new Container()
DI.bind<Config>(TYPES.Config).toConstantValue(config() as Config)
DI.bind<IZoneService>(TYPES.ZoneService).to(ZoneService).inSingletonScope()

export {DI}

// Types.ts
import "reflect-metadata";

export const TYPES = {
  Config: Symbol.for("Config"),
  ZoneService: Symbol.for("ZoneService"),
}

// ZoneService.ts
import {TYPES} from "../../di/Types";
import {inject, injectable} from "inversify";
import {Config} from "../../models/Config";
import {IZoneService} from "../interfaces/IZoneService";

const TAG = "ZoneService"

@injectable()
class ZoneService implements IZoneService {

  public constructor(
    @inject(TYPES.Config) config: Config
  ) {
    ...
  }
  ...
}

// ZoneInteractor.js
const {DI} = require("../di/inversify.config");
const {IZoneService} = require("../services/interfaces/IZoneService");
const {TYPES} = require("../di/Types");

class ZoneInteractor {
  async run() {
    ...
    const zoneService = DI.get<IZoneService>(TYPES.ZoneService) // Issue 1
    zoneService.doSomething() // Issue 2
  }
}

Issue 1 : I am getting Cannot convert a Symbol value to a number on the line, if anyone has an insight here, it would be very helpful.

If I change TYPES object for string only like this :

export const TYPES = {
  Config: "Config",
  ZoneService: "ZoneService",
}

I am getting the issue related to this thread, Issue 2 : zoneService.doSomething is not a function, do you understand what could be the issue with imports here ?

tperraut avatar Aug 11 '21 00:08 tperraut

Hello. I am in a trip, so will be brief.

The issue is with library consumers code, with my code in this case. I suggest checking the dependency for nullish values before getting name property and throwing a more helpful exception (see comments above). I think giving the name of faulty dependency with a start trace can really speed up circular dependencies detection in user's code.

I don't think the library can resolve this issue any other way than throwing a more helpful error.

On Wed, Aug 11, 2021, 3:12 AM Thomas Perraut @.***> wrote:

@shevchenkobn https://github.com/shevchenkobn I am sorry but after reading your answer multiple times and checking your code and jimador one, I still do not understand how to fix the issue.

Here is what I have :

// inversify.config.tsimport {TYPES} from "./Types";import {Container} from "inversify";import {Config} from "../models/Config";import {ZoneService} from "../services/zones/ZoneService";import {IZoneService} from "../services/interfaces/IZoneService"; const DI = new Container()DI.bind<Config>(TYPES.Config).toConstantValue(config() as Config)DI.bind<IZoneService>(TYPES.ZoneService).to(ZoneService).inSingletonScope() export {DI} // Types.tsimport "reflect-metadata"; export const TYPES = { Config: Symbol.for("Config"), ZoneService: Symbol.for("ZoneService"),} // ZoneService.tsimport {TYPES} from "../../di/Types";import {inject, injectable} from "inversify";import {Config} from "../../models/Config";import {IZoneService} from "../interfaces/IZoneService"; const TAG = "ZoneService"

@injectable()class ZoneService implements IZoneService {

public constructor( @inject(TYPES.Config) config: Config ) { ... } ...} // ZoneInteractor.jsconst {DI} = require("../di/inversify.config");const {IZoneService} = require("../services/interfaces/IZoneService");const {TYPES} = require("../di/Types"); class ZoneInteractor { async run() { ... const zoneService = DI.get<IZoneService>(TYPES.ZoneService) // Issue 1 zoneService.doSomething() // Issue 2 }}

Issue 1 : I am getting Cannot convert a Symbol value to a number on the line, if anyone has an insight here, it would be very helpful.

If I change TYPES object for string only like this :

export const TYPES = { Config: "Config", ZoneService: "ZoneService",}

I am getting the issue related to this thread, Issue 2 : zoneService.doSomething is not a function, do you understand what could be the issue with imports here ?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/inversify/InversifyJS/issues/1005#issuecomment-896395949, or unsubscribe https://github.com/notifications/unsubscribe-auth/AF23D62L4DTOXNLCLABPU3DT4G553ANCNFSM4GGIDUQA . Triage notifications on the go with GitHub Mobile for iOS https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Android https://play.google.com/store/apps/details?id=com.github.android&utm_campaign=notification-email .

shevchenkobn avatar Aug 11 '21 08:08 shevchenkobn

@sergeyzenchenko thank you for dropping by, my issue was way simpler than I thought, I just added a <Type> in a js file, breaking everything at runtime. I described the issue here : #1364

tperraut avatar Aug 13 '21 09:08 tperraut

I found the same problem. My problem has been solved, found out that other classes were imported before importing the container configuration file。

export * from './loader'
export * from './add.command'
export * from './create.command'
export * from './info.command'
export * from './open.command'```
The above is how I write it. The first line introduces the IOC container, and the last few lines are injectable classes

yyi0708 avatar Apr 07 '23 01:04 yyi0708