1

I'm using TypeScript + AMD (RequireJs) and I'm facing a problem regarding circular dependency. tsc deals with circular dependency pretty smooth but when it's run time, it took me hours to figure out that I'm facing a circular dependency problem.

Here's my scenario:

Singleton.ts

import Window = require("./Window");

class Singleton
{
    private w : Window;
    private counter : number = 0;

    constructor() {
        w = new Window();
    }

    Increment() : number {
        return ++this.counter;
    }
}
export = Singleton;

Window.ts

import Singleton = require("./Singleton");
declare var global_instance : Singleton;

class Window
{
    private my_number : number = 0;

    constructor() {
        this.my_number = global_instance.Increment();
    }
}
export = Window;

As simple as it seems, it leads to a circular dependency and thus I'm unable to implement it in a browser. But as I said tsc deals with it perfectly without any problem. As I searched the Internet I found a couple of proposed solutions:

This one suggests adding a typeof before the type to prevent compiler from actually putting the type's file in require list. But it results in a dozen of compiler errors so it's out.

This one sounds promising but it's a RequireJs solution and I'm not sure how to implement it in TypeScript!

Does anyone have any solution how to deal with circular dependency while benefiting from type checking of tsc compiler?

4
  • This sounds stupid, but do you really need to introduce circular dependency? Shouldn't the design/code be written so that you don't have it in the first place? Commented Jul 30, 2014 at 20:12
  • It would be perfect if you could point me to the right direction. How would you design the mentioned scenario? Unless you want to duplicate Singleton's class into an interface which is redundant and my last resort. I'm hoping to find something more practical. Commented Jul 30, 2014 at 20:18
  • How'd you do it in something like C# where you can't have circular dependency? Where does the instance count really belong? Why does the Window have to increment some other class's count? Commented Jul 30, 2014 at 20:22
  • 2
    I feel your pain. Because circular dependencies are hard to tackle in (modular) Javascript, and an even greater pain in Typescript, some people have invented a new "Pattern": if you have them, your design is wrong. Don't listen, 99% of Java code would not run if this were true. There are a few workaround in Javascript, less or none in Typescript. I created this issue some time ago, but it did not trigger much interest. I had to ditch typescript for a project due to this. Good luck. Commented Jul 30, 2014 at 20:59

2 Answers 2

2

Because these files are absolutely tied together, put them in a single file. Not only does it solve your circular reference, but it also means one less HTTP request (which you are guaranteed to need).

Once they are in the same file, you may decide to merge the concepts further, or split out that part that they both depend on to remove the circular dependency.

Sign up to request clarification or add additional context in comments.

3 Comments

+1. Never a good idea to leave a circular dependency between files
Atom TypeScript now does circular dependency analysis : github.com/TypeStrong/atom-typescript/blob/master/docs/…
@Sohnee Having two files that reference each other is a very common thing in software architectures and especially javascript. Following your advice of combining everything into a single file would be a disaster. 80% of the code of a large app would end up in the same file. Sure i'm going to combine into single file for distribution, but modules need to be able to be separate files, and discreet. The fact that TypeScript cannot handle circular references, is definitely a serious problem. The solution is most certainly not putting everything in one file.
0

if you are using the requirejs.d.ts

amend it with this

declare var exports: any;

and then your class you explicitly call exports.

import Window = require("./Window");

class Singleton
{
    private w : Window.Window;
    private counter : number = 0;

    constructor() {
        w = new Window.Window();
    }

    Increment() : number {
        return ++this.counter;
    }
}
exports.Singleton = Singleton;
export = Singleton;

Window.ts

import Singleton = require("./Singleton");
declare var global_instance : Singleton.Singleton;

class Window
{
    private my_number : number = 0;

    constructor() {
        this.my_number = global_instance.Increment();
    }
}
exports.Window = Window;
export = Window;

I wish there was a way, such as in node to just set exports directly but that does not seem to work, at least for me.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.