Consider the sample of inter-dependent code (below) that makes use of decorators.
Now consider the following workflow (yes, I do want to pass the actual exported cla
If you can postpone actions performed in decorator - you can overcome the limitation and break circular dependency. From the names of your real decorators @HasMany and @BelongsTo it looks like you are attaching some sort of the metadata to each class for the later usage - if so here is my suggestion:
export function Test(passedClass: Function | string)
I assume here that the decorator will store meta information in some sort of static dictionary, like: . Where properties can look like
{
hasMany: {new(): any} | string
belongsTo: {new(): any} | string
}
Inside decorator create new properties object with hasMany/belongsTo properties set to passedClass
. If passedClass
is not string - check all already added properties and replace any hasMany/belongsTo that are of string type and equal current passedClass.name
Remove reference to Child from Parent.
This is somewhat naive implementation and you can implement some private fields instead to hide the intermediate string data and keep away from exposing union type fields.
Hope this will help you.
How about doing the same, but structuring your code differently?
If both Child
and Parent
reside in the same file then it shouldn't be a problem.
That might not sound optimal as it's comfortable to separate code to modules due to length and logic, but that can be solved in a way.
You can have a main file that has base classes for those, even abstract:
// Base.ts
import {Test} from "./Decorators";
@Test(BaseChild)
export abstract class BaseParent {}
@Test(BaseParent)
export abstract class BaseChild {}
And then in your specific modules:
// Parent.ts
import {BaseParent} from "./Base";
export class Parent extends BaseParent {}
And
// Child.ts
import {BaseChild} from "./Base";
export class Child extends BaseChild {}
Hit the same problem today. I solved it slightly differently, by replacing @Test(Parent)
by @Test(() => Parent)
.
Instead of tracking the class constructor (Parent
) in metadata, I track the thunk that returns the constructor (() => Parent
). This delays the evaluation of the Parent
imported variable until the thunk is invoked, which does the trick.