简体   繁体   中英

loader exception / circular reference by decorators

Question

I get a circular loader exception. This is may caused by the compiler option "emitDecoratorMetadata":true . How can I fix it? Thanks for helpful replays!

Introduction

I have prepared a minimal project for reproducing the error. Please take a look at my temporary git repository: git repo for bug presentation

I use two libraries ( typeorm and json2typescript ) and both operate with decorators. I use multiple decorators from both libraries on some class properties.

Steps for reproducing:

  1. Clone the git repo.
  2. Install all packages by the command npm i (npm 6.9.0).
  3. Open the root directory by Visual Studio Code .
  4. Open bugexample/test/test.spec.ts , go to the debug view and start debugging by the config Mocha current file .

After these steps you should see an exception output.

/bugexample/node_modules/reflect-metadata/Reflect.js:553
                var decorated = decorator(target, propertyKey, descriptor);
                                ^
Error: Fatal error in JsonConvert. It is not allowed to explicitly pass "undefined" as second parameter in the @JsonProperty decorator.

        Class property: 
                banana

Use "Any" to allow any type. You can import this class from "json2typescript".

The property banana gets the type Banana as parameter and this type is undefined for unknown reasons. The library json2typescript is not the cause of this problem.


Analysis

Now I want to breakdown the issue. I begin with the two model classes and end with the test.

At first, please take a look at bug_presentation/src/persistence/models/ape.model.ts .

import { JsonObject, JsonProperty } from "json2typescript";
import { Column, Entity, OneToOne, PrimaryGeneratedColumn } from "typeorm";

import { Banana } from "./banana.model";

/**
 * This is an entity class.
 * 
 * @author Tim Lehmann <l_@freenet.de>
 */
@JsonObject("Ape")
@Entity()
export class Ape {

  @PrimaryGeneratedColumn()
  readonly id: number

  @JsonProperty('0')
  @Column()
  readonly name: string = null

  // the associated table holds the foreign keys

  @JsonProperty('1', Banana)
  @OneToOne(type => Banana, banana => banana.possessionOf, { cascade: true })
  readonly banana = new Banana();
}

In line 24 the type Banana is the passed parameter but for unknown reasons it's undefined for the current test at this time.

Now take a look at bug_presentation/src/persistence/models/banana.model.ts , please.

import { JsonObject, JsonProperty } from "json2typescript";
import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm";

import { Ape } from "./ape.model";

/**
 * @author Tim Lehmann <l_@freenet.de>
 */
@JsonObject("Banana")
@Entity()
export class Banana {

  @PrimaryGeneratedColumn()
  private readonly id: number

  @JsonProperty('0')
  @Column()
  readonly weight: string = null

  @OneToOne(type => Ape, possessionOf => possessionOf.banana)
  @JoinColumn({ name: "possessionOf" })
  readonly possessionOf: Ape = new Ape();
}

Line 21 and 22 are problematic. If I comment these lines out then there is no loader exception.

Please take a look at bug_presentation/test/test.spec.ts , lastly.

import { expect } from "chai";

import { Ape } from "../src/persistence/models/ape.model";
import { Banana } from "../src/persistence/models/banana.model";

// const classApe = Ape;
const classBanana = Banana;

describe("check if class type exist", () => {

  it('check class Ape is defined', () => {
        // expect(classApe).exist;
  })

  it('check class Banana is defined', () => {
    expect(classBanana).exist;
  })
})

I want to test that the type/class Banana isn't undefined, but the test breaks earlier because the library json2typescript throws an exception if the passed property (in this case type Banana ) is undefined .

The strange behavior is that if I assign the class Ape to a variable (remove the comment at line 6) then the type/class Banana is defined.


I know I'm a bit late on this one but I ran into a similar issue and found this post. After digging around I realized it wasn't a problem with json2typescript at all, rather a circular dependency issue.

I found this pretty useful article which helped me fix the issue in my project: https://medium.com/visual-development/how-to-fix-nasty-circular-dependency-issues-once-and-for-all-in-javascript-typescript-a04c987cf0de

Essentially, the problem is:

  • In ape.model.ts you import banana: import { Banana } from "./banana.model";
  • In banana.model.ts you import ape: import { Ape } from "./ape.model";

These modules are loaded in serially, so when the first gets loaded (lets pretend its ape.model.ts ) it tries to import banana -> Banana gets loaded which tries to import ape -> ape hasn't finished being loaded so it returns undefined.

The solution the article I linked to proposes you create a internal.ts file for managing module loading:

export * from './ape.model';
export * from './banana.model';

then always load from internal, ie in ape.model.ts :

import { Banana } from './internal';

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM