Search code examples
node.jstypescripttypeormnodemonts-node

Cannot access variable before initialization error when variable is initialized in another file


I have something like this:

photo.ts

import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from "typeorm"
import { User } from "./User"

@Entity()
export class Photo {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    url: string

    @ManyToOne(() => User, (user) => user.photos)
    user: User
}

user.ts

import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from "typeorm"
import { Photo } from "./Photo"

@Entity()
export class User {
    @PrimaryGeneratedColumn()
    id: number

    @Column()
    name: string

    @OneToMany(() => Photo, (photo) => photo.user)
    photos: Photo[]
}

Am running using nodemon and i have ts-node installed, but am getting the following error:

ReferenceError: Cannot access 'User' before initialization
    at file:///path/to/project/src/database/entities/photo.ts:14:9
    at ModuleJob.run (node:internal/modules/esm/module_job:194:25)

This error beats me as I don't even know where to start because, frankly, I have never encountered the error before, any help would be greatly appreciated.

Versions:

node: v18.17.1
ts-node: v10.9.1
nodemon: v3.0.1
typescript: v5.2.2

Solution

  • The problem is that your two modules have a cyclic relationship and they both try to use the other's exported member during the initial module execution. So one or the other has to be run first (photo.ts in your example) and when it tries to use User, that exported member hasn't been initialized yet because user.ts hasn't been executed yet. I have to admit I'm slightly surprised you're getting this as a runtime error because the only place I see them being used in a cycle at the top level of the module code is in the types being applied to user and photos. Perhaps it has something to do with what the decorators are doing, or to the fact that User and Photo are both types and values (constructor functions).

    The TypeORM website talks about "circular relationships" here. It says to use the Relation type to avoid this (so user: Relation<User> rather than user: User, and photos: Relation<Photo[]> rather than photos: Photo[]). Apparently this prevents the cycle from arising.