sequelize-typescript: Cannot access [Model] before initialization
Versions
- sequelize: 4.44.4
- sequelize-typescript: 1.1.0
- typescript: 3.9.2
I’m submitting a …
[X] bug report [ ] feature request
Actual behavior:
$ ./node_modules/.bin/ts-node test.ts ReferenceError: Cannot access ‘Team’ before initialization
Expected behavior:
No error.
Steps to reproduce:
Use (basically) the code from the README (https://github.com/RobinBuschmann/sequelize-typescript#one-to-many) as test.ts
:
import {BelongsTo, Column, ForeignKey, HasMany, Model, Table} from "sequelize-typescript";
@Table
export class Player extends Model<Player> {
@Column
name!: string;
@Column
num!: number;
@ForeignKey(() => Team)
@Column
teamId!: number;
@BelongsTo(() => Team)
team!: Team;
}
@Table
export class Team extends Model<Team> {
@Column
name!: string;
@HasMany(() => Player)
players!: Player[];
}
tsconfig.json:
{
"compilerOptions": {
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"lib": ["es2019", "es2020.promise", "es2020.bigint", "es2020.string"],
"noImplicitAny": true,
"module": "commonjs",
"outDir": "_dist",
"rootDir": ".",
"skipLibCheck": true,
"sourceMap": false,
"strict": true,
"strictNullChecks": true,
"target": "ES2019"
}
}
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 5
- Comments: 23
I’ve got another workaround.
I’ve noticed that it is possible to reference classes declared later when defining arrays
Which brought me to this:
Compiles without errors.
Nice find @strrife!
The following alternative works too.
The root cause of all these issues is the requirement of recursive imports. This PR (https://github.com/RobinBuschmann/sequelize-typescript/pull/1206) solve that in part. But in order to completely solve this, I think the returned reference from an association should be a generic model based on the interface.
So, the final solution could look something like this:
The issue still happens for me when models are in different files but only when
module
in tsconfig.json is set to any ES version. It does not happen when targeting CommonJS. Maybe @TPME and @EdByrnee can confirm whether they’re also targeting ESM. It seems like using the() => Model
as a workaround for avoiding circular dependencies only works in CommonJS.I have all my entities in different files but I’m getting the same issue?!
I was using
@nestjs/sequelize
. Got same error. Updatingmodule:es2020
andtarget:es2015
tomodule: commonjs
andtarget: es2017
solved the issue.Hi,
I have the same issue and it’s really blocking me. The weird part is that it seems very inconsistent.
Yet I still have this error. Please help 😕
i did change the Model to Model[] and it works… or adding union types null also work
in my case i was trying some dirty tricks which brought upon a flawed table design that was given to me in my project while still using ORM. I get this problem when trying to re use the many-to-many intermediate table as a bridge to another table.
In my case changing the order of how you import these models on the same file you use
new Sequelize()
solved the problem, I’d suggest having a look at this repo’s example because it does the exact same thing and their version works even though it needs updating to the latest version.I’m trying to remove emitDecoratorMetadata option. If you set types/table names manually it can be a solution as well
Similar issue, only thing I could get to work was to add using belongsTo directly from created Sequelize instance
// instance sequelize = Sequelize({config: {models: … } })
// create the relationship sequelize.model(Player).belongsTo(Team);
TL;DR: see workaround code snippet.
Without in-depth knowledge of the code I’m not sure if this could work but I’m suggesting it anyways if someone wants to try to implement it.
One way of solving the issue might be to allow passing just the model name on one side of the association and keep passing the model getter on the other (basically a TS overload + a
typeof modelGetter === 'string'
check). This way the circular dependency would be avoided (no import required on one side in emitted JS code) and sequelize-typescript could resolve the model constructor from its name from the already registered models. One problem with this approach is what would happen if the model associating by name is defined before the one associating by getter: in that case sequelize-typescript wouldn’t have any constructor to resolve yet… And this isn’t really under the user’s control since the import order is established by Node.With that said unfortunately I don’t have the time to create a PR myself at the moment but I might do it some time in the future since I will also need this feature. In the meantime maybe @RobinBuschmann has some suggestions on the feasibility of this approach/other possible solutions…
For now though I also suggest a workaround I’m planning on using myself: It’s possible to associate model A with model B normally in A.ts (using the import + decorator). Then leave out the association completely in B.ts and add the association for B in A.ts using regular old sequelize.
Here’s an example:
It’s not super pretty but it should work. Let me know what do you think/any possible issue with either approach.
It does not because changes the target type to commonjs instead of ESM which is not what we want. If you want to build an ESM module that is not a solution!
I resolved that just putting each model in different files. It is not a sequelize issue, seems like the problem is on typescript compilation with
emitDecoratorMetadata: true
option.