Classes In Wrong Order In Single File with Inheritance and Circular Reference

Topics: General
Apr 18, 2013 at 3:33 PM
I think I've discovered a bug in the compiler where classes compiled into a single output file aren't listed in the correct order when circular references including inheritance is involved. (Think A references B, B references C, C extends A.) The bug report hasn't been acknowledged yet so I'm wondering if that wasn't the right way to report it, or if it's a known issue I didn't find in the search.

If it is a known issue does anybody have a workaround more satisfying than breaking the chain of reference by treating something as an "any"?

May 17, 2013 at 4:46 PM
Edited May 17, 2013 at 4:48 PM
I have the same issue.

As a preface, there are 2 types of dependencies: Javascript (hard) and TypeScript(soft). TypeScript dependencies are dependencies that are needed to understand the definition during compile. Javascript dependencies are dependencies that are needed when trying to parse the document. Especially when using inheritance, it is imperative that Class A come sequentially before Class B if Class B extends Class A.

Before Typescript came about, I was dealing with similar issues when trying to ensure Visual Studio Intellisense had all the information it could get. I built a tool that merges all files into one in the correct order so that these dependencies resolved properly. The tool parses the header of the file and smashes them all together into 1 *.ts file. Here is how it works...

All hard dependency references are listed first. I then have a separator comment line. Then all soft references are listed. Time for an example.

File: A.ts
class A {
   static DoSomething() { }
File C.ts
/// CODE
/// <reference path="A.ts" />

class C {
   constructor() {
File: B.ts
/// <reference path="A.ts" />
/// CODE
/// <reference path="C.ts" />

class B extends A {
TypeScript can handle circular references by just stopping any circular paths and it reads all reference paths as if the "CODE" comment line didn't exist. The tool uses it for its own ordering. I then just run tsc on the final output.
May 17, 2013 at 11:25 PM

I'm not sure whether you're reporting a bug or not, but when I try your sample the TypeScript compiles (and combines using the -out parameters) the files as expected.
class A{}
class C{}
class B{}
Is that not what you're seeing?
May 22, 2013 at 1:59 AM
My example doesn't correctly illustrate the issues I have had. As I took a different approach, let me try to reproduce. Ultimately, I'm seeing cases where the typescript compiler compiles successfully; however, when run in the browser, the javascript engine crashes in the _extends method because _ is undefined in the following code.
var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
Stay tuned for an example.
May 23, 2013 at 4:48 PM
Edited May 23, 2013 at 4:49 PM
I am having a tough time reproducing in a small example. My project consists of a large amount of files. So many that if I use the built-in visual studio build mechanism, the compiler errors out because the command is so long and gets truncated. Instead, I have to spit out the list of files to a file that I can then run tsc on. In doing so, I inevitably get the error above. If I change the ordering the list of files to correctly match dependencies, all is well.
Jul 15, 2013 at 9:49 PM
Edited Jul 16, 2013 at 8:00 AM
We have the same problem and it seems to be a compiler bug in TypeScript - this became apparent when we starting to test the classes we have converted to typescript.

A lot of the classes in our partition and entities modules have this bug:

We have also created a simple test case to isolate the bug:

These classes in question all have dependant resources the compiler cannot resolve, the resulting / exported JavaScript will fail. Reason being the compiler __extends and references an undefined prototype chain for an object that has not yet been defined. Which is what this little helper does :
var __extends = this.__extends || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype; //Uncaught TypeError: Cannot read property 'prototype' of undefined <----- Error Occurs here.
    d.prototype = new __();
So, for example, if class FooA extends Base… and FooC extends FooA - whichever code block gets placed first in the JS will cause an extend error - this is because JS injects the proto chain to extend and that object is not yet defined.

Hope this help... ( and gets resolved at somepoint ). We are actively looking at solutions and will report back if we find anything. At the moment it looks like AMD might be our best candidate - which is something we had hoped to avoid for a 3D lib.
Jul 15, 2013 at 11:55 PM
There is an existing work item for this bug here.

NTaylorMullen has helpfully shared a powershell script you can use in your build process to work around this issue in the meanwhile.
Jul 16, 2013 at 8:13 AM
Thank you for the reply - have added a comment to the other thread. It seems this bug is slightly different as no matter which code block gets places first it will fail.