The issue of "ReferenceError: Cannot access '<Entity>' before initialization" occurs when using a OneToMany relationship with TypeORM

There are two main actors involved in this scenario: User and Habit. The relationship between them is defined by a OneToMany connection from User to Habit, and vice versa with a ManyToOne.

User Entity

import {Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, BeforeInsert, BeforeUpdate, OneToMany} from "typeorm";
import * as bcrypt from "bcryptjs";
import { Habit } from "../habits/habits.entity";

@Entity()
export class User {
    @PrimaryGeneratedColumn("uuid")
    id: string;

    @Column()
    name: string;

    @Column()
    email: string;

    @Column()
    password: string;

    @OneToMany(type => Habit, habit => habit.author)
    habits: Habit[];

    @CreateDateColumn()
    dateCreated: Date;

    @UpdateDateColumn()
    dateUpdated: Date;

    @BeforeInsert()
    @BeforeUpdate()
    async hashPassword(): Promise<void> {
        this.password = await bcrypt.hash(this.password,10);
    }

    async comparePassword(password: string): Promise<boolean> {
        return bcrypt.compare(password, this.password);
    }

    constructor(props: any) {
        Object.assign(this, props);
    }
}

Habit Entity

import {Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn, ManyToOne} from "typeorm";
import { User } from "../users/users.entity";

@Entity()
export class Habit {
    @PrimaryGeneratedColumn("uuid")
    id: string;

    @Column()
    name: string;

    @Column({ nullable: true})
    description?: string;

    @ManyToOne(type => User, user => user.habits)
    author: User;

    @CreateDateColumn()
    dateCreated: Date;

    @UpdateDateColumn()
    dateUpdated: Date;

    constructor(props: Partial<Habit>) {
        Object.assign(this, props);
    }
}

Issue Encountered

During the setup of the aforementioned relationships, there was an error detected:

WARNING in Circular dependency detected:
apps\api\src\habits\habits.entity.ts -> apps\api\src\users\users.entity.ts -> apps\api\src\habits\habits.entity.ts

WARNING in Circular dependency detected:
apps\api\src\users\users.entity.ts -> apps\api\src\habits\habits.entity.ts ...

ReferenceError: Cannot access 'User' before initialization ...

[Additional error messages displayed]

Note

The development environment utilizes Nx for project management and NestJS for application development. TypeOrm version in use is "^0.2.22", and @nestjs/typeorm version is "^6.2.0"

The configuration within the tsconfig.json file is provided below:

{
  "compileOnSave": false,
  ...
[Remaining tsconfig settings]
    "@awhile/ui": ["libs/ui/src/index.ts"]
  }
}

Prior suggestions like utilizing a ManyToMany relation and altering the ECMAScript target version have been attempted without resolving the issue. Notably, this reference error does not occur in a separate NestJS app that does not utilize Nx.

The entities are strictly used within their services and face no instantiation elsewhere.

Any insights or assistance would be greatly appreciated. Thank you for your time.

Answer №1

Make use of the Relation type wrapper to prevent circular dependency issues, as detailed in this resource.

Here's an example:

import {Entity, OneToMany, Relation} from "typeorm";
import {Habit} from "../habits/habits.entity";

@Entity()
export class User {
    ...

    @OneToMany(type => Habit, habit => habit.author)
    habits: Relation<Habit>[];

    ...
}

Answer №2

By updating the tsconfig file to set the target as "esnext" and module as "commonjs", the issue was successfully resolved.

{
  "extends": "../../tsconfig.json",
  "compilerOptions": {
    "types": ["node", "jest"],
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "module": "commonjs",
    "target": "esnext"
  },
  "include": ["**/*.ts"]
}

Answer №3

My approach to solving this problem involved utilizing the autoLoadEntities: true feature when loading the TypeORM configuration into NestJS. It's important to note that this feature is specific to NestJS and won't work if you are using ormconfig.json.

You can find more information about the autoLoadEntities property in the documentation here: https://docs.nestjs.com/techniques/database#auto-load-entities

UPDATE 04/10/2020

I continued to encounter issues with relations, so I came up with another solution. Although it may not adhere to all standards, I consolidated all Entities into a single file, exported them from there, and imported them as needed.

It's worth noting that the order in which the classes are declared plays a crucial role in this workaround.

Answer №4

To achieve the same result as @jdruper, consider utilizing a barrel file approach. If your tsconfig.json does not contain a paths property, you can add one under compilerOptions with the following structure:

"paths": {
   "@entities":["app/src/entities/index.ts"]
}

Place all your entity classes in the entities folder, create an index.ts file, and include export statements within it:

export * from './user.entity';
export * from './habit.entity';

Ensure that the export statements are ordered correctly. Subsequently, your imports should resemble this pattern.

import { User, Habit } from '@entities';

For further details on working with barrel files, you can search for typescript barrel file.

If you are using NX, you have the option to create an entities lib which serves the same purpose.

This process may involve consolidating all entity files together and updating imports in various modules and services. While not perfect, it is preferable to keeping everything in a single file.

Answer №5

In my situation, I encountered the following code:

export class CreateUser {
  @IsNotEmpty()
  userName: string
  @IsNotEmpty()
  photoURL: string
  @IsNotEmpty()
  displayName: string
  ...
  @IsDefined()
  @IsNotEmptyObject()
  @ValidateNested()
  @Type(() => CreateStreakConfig)
  streakConfig: CreateStreakConfig
}

export class CreateStreakConfig {
  @IsNotEmpty()
  minimumDistance: number
  @IsNotEmpty()
  unitOfDistanceType: typeof STREAK_CONFIG_UNIT_OF_DISTANCE_MILES | typeof STREAK_CONFIG_UNIT_OF_DISTANCE_KILOMETERS
}

It seems that during NestJs initialization, each object is instantiated in sequence. Therefore, resolving my issue was as simple as rearranging them:


export class CreateStreakConfig {
  @IsNotEmpty()
  minimumDistance: number
  @IsNotEmpty()
  unitOfDistanceType: typeof STREAK_CONFIG_UNIT_OF_DISTANCE_MILES | typeof STREAK_CONFIG_UNIT_OF_DISTANCE_KILOMETERS
}

export class CreateUser {
  @IsNotEmpty()
  userName: string
  @IsNotEmpty()
  photoURL: string
  @IsNotEmpty()
  displayName: string
  ...
  @IsDefined()
  @IsNotEmptyObject()
  @ValidateNested()
  @Type(() => CreateStreakConfig)
  streakConfig: CreateStreakConfig
}

I hope this information proves helpful to others facing a similar challenge!

Answer №6

When working with ESM in TypeScript projects, it is recommended to utilize the Relation wrapper type for relation properties in order to prevent circular dependency problems.

Learn more about handling relations in ESM projects here

Answer №7

Make sure to update the nest-cli.json file with the following changes:

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true,
    "webpack": true
  }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

ESLint detecting error with returning values in async arrow functions

Currently facing a minor inconvenience instead of a major problem. Here is the code snippet causing the issue: export const getLoginSession = async (req: NextApiRequest): Promise<undefined | User> => { const token = getTokenCookie(req) if (!t ...

Struggling to understand how to properly 'map' my data from the response in Next.js 13 using Typescript

Just a few days into my journey with next.js, and I'm already facing a challenge in figuring out how to fetch data from an API. In regular React, I would typically use useState and useEffect for managing state and fetching data. But when it comes to n ...

Error: Unable to access the 'filter' property as it is undefined. TypeError occurred

findLoads(){ if(this.loggedInUser.userFullySetupFlag === 0 || this.loggedInUser.businessFullySetupFlag === 0){ swal( 'Incomplete Profile', 'To find loads and bid, all the details inside User Profile (My Profile) and Business Profil ...

Creating a custom React hook in TypeScript to handle mouse events

I have been working on creating a custom hook in TypeScript/React, and I am looking to convert the code snippet below into a custom hook. Currently, I am passing handleClick to the onClick attribute in a div element to detect user clicks and route them to ...

When attempting to pass an array of objects to a child component, TypeScript raises an error regarding types

Hey everyone, I'm currently facing an issue while trying to pass an array of objects as props. Here's the content of my data.json file: [ { "category": "Reaction", "score": 80, "icon": " ...

Control or restrict attention towards a particular shape

Greetings! I am seeking guidance on how to manage or block focus within a specific section of a form. Within the #sliderContainer, there are 4 forms. When one form is validated, we transition to the next form. <div #sliderContainer class="relativ ...

Absent observable functions in the RxJS 5.0.0-beta.0 release

I am encountering an issue when trying to use RxJS with Angular 2. The methods recommended from the Typescript definition file are not present on my Observable object... https://i.stack.imgur.com/6qhS4.png https://i.stack.imgur.com/m7OBk.png After inves ...

What is the process of substituting types in typescript?

Imagine I have the following: type Person = { name: string hobbies: Array<string> } and then this: const people: Array<Person> = [{name: "rich", age: 28}] How can I add an age AND replace hobbies with a different type (Array< ...

What allows the type expression to be considered valid with a reduced amount of arguments?

Currently diving into Typescript and focusing on functions in this unit. Check out the following code snippet: type FunctionTypeForArrMap = (value: number, index: number, arr: number[]) => number function map (arr: number[], cb: FunctionTypeForArr ...

The 'checked' property cannot be bound to 'mat-button-toggle' as it is not recognized as a valid property in Angular 9

I am encountering an issue with my Angular 9 application. I have integrated angular-material and imported the MatCheckboxModule correctly in the module. Here is the version of the material package I am using: "@angular/material": "^10.2.0&q ...

Encountering an error during the registration process of @fastify/middie

I am currently in the process of integrating Fastify into a small project I am working on. One of the key requirements for this project is the utilization of Middleware (via @fastify/middie). However, when I follow the necessary steps to register the middi ...

Set the default value for a form control in a select dropdown using Angular

I've been struggling to figure out how to mark an option as selected in my select element, but I haven't had any luck. I've tried multiple solutions from the internet, but none of them seem to be working for me. Does anyone out there have ...

Running nestjs-console commands within an Angular/nx workspace environment can be easily achieved by following these steps

I have integrated a NestJS application within an Angular / Nx workspace environment. For running commands in the Nest application, I am utilizing nestjs-console (e.g., to load fixture data). As per the instructions in the nestjs-console documentation, th ...

Typescript-powered React component for controlling flow in applications

Utilizing a Control flow component in React allows for rendering based on conditions: The component will display its children if the condition evaluates to true, If the condition is false, it will render null or a specified fallback element. Description ...

What is the correct method for typing a React functional component with properties?

Here's a react functional component I have created... const MyFunction = () => { // lots of logic MyFunction.loaded = someBoolean; return <div>just one line</div> } MyFunction.refresh = () => ....... I added two properti ...

Ways to dynamically apply styles to the component tag depending on the visibility of its content

Consider a scenario where you have a component with logic to toggle the visibility of its contents: @Component({ selector: 'hello', template: `<div *ngIf="visible"> <h1>Hello {{name}}!</h1></div>`, styles: [`h1 { fo ...

Bringing in a module that enhances a class

While scouring for a method to rotate markers using leaflet.js, I stumbled upon the module leaflet-rotatedmarker. After installing it via npm, I find myself at a loss on how to actually implement it. According to the readme, it simply extends the existing ...

Issues with navigation in React Native Typescript

Currently, I am in the process of developing a new React Native Expo project utilizing TypeScript. I have been attempting to configure navigation following the guidance provided in React Native's TypeScript documentation. However, upon running and sim ...

Angular: Utilizing the new HttpClient to map HTTP responses

Within my application, I have a standard API service that communicates with the backend using requests structured like this: post<T>(url: string, jsonObject: object): Observable<T> { return this.http.post<T>(url, JSON.stringify(json ...

Leveraging NgRx for Managing Arrays

export class Ingredient { public name: string; public amount: number; constructor(name: string, amount: number) { this.name = name; this.amount = amount; } } List of Ingredients: export const initialIngredients: Ingredient ...