Circular dependency / referencing in typescript

I have 3 classes (A, B, C)
A.ts

import B from "./B";
const {ccclass, property} = cc._decorator;
@ccclass
export default class A extends cc.Component {
    @property(B)
    refB: B = null;
}

B.ts

import C from "./C";
const {ccclass, property} = cc._decorator;
@ccclass
export default class B extends cc.Component {
    @property(C)
    refC: C = null;
}

C.ts

import A from "./A";
const {ccclass, property} = cc._decorator;
@ccclass
export default class C extends cc.Component {
    @property(A)
    refA: A = null;
}

due to circular dependency / referencing, either refA / refB/ refC will be null in IDE. how to fix this problem in typescript?

Well, you don’t. This should not happen. It’s a code design flaw.

If you need to set as a property: Create a D.ts file that is a singleton, stores refs to A B and C, and get the refs from there.

If you DON’T need to set as a property: Require it inside the function that will work with that, something like

asd() {
    import("A.ts").then(A => {
        let refA = this.nodeA.getComponent(A);
        ...
    });
}
1 Like

I disagree with the “code design flaw”, this is a common pattern to keep componentized model when you deal with some parts of code (I see this a lot in Unity when creating componentized UI systems).

My recommendation @smellypotato is to have the properties as cc.Node, which can be assigned in the inspector, and during the onLoad function do a “getComponent” in these nodes and store the result in another variable

1 Like

I would love to use your approach, unfortunately once I call getComponent(A/B/C), the refA/refB/refC will be null in IDE. Does it work on your code?

Hope I’m not sending you on a wild goose chase, and I haven’t tried this, but just thinking through the problem you may be able to crudely get around this:

  1. Define properties as Nodes
  2. Create interfaces from the classes you need
  3. Call getComponent with a string instead of the type, cast to the interface.

So something like this:

In B.ts declare:

export interface InterfaceB extends B {}

In A.ts, do something like this

// Do NOT import B, just the interface
import { InterfaceB } from "./B"

@ccclass
export class A extends cc.Component {
	@property (cc.Node)
	nodeB: cc.Node = null;

	public someMethod(): void {
		let propB = A.getComponent("B") as InterfaceB;
	}
}

You would do these for each of the classes that had a circular dependency (I suppose you technically only need to do this for one, since the last one creates the loop? I dunno …)

Like I said, I have not tried this, so hopefully I’m not sending you on a wild goose chase. I just know how frustrating these stupid TS circular dependencies can be and was hoping to possibly help out. If this doesn’t work, maybe it’ll help you on the path to figuring out something that does.

1 Like

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.