9

I'm compiling my TypeScript project into a single JS file, at the moment by specifying an outFile in the compiler options. In my project each class is defined in a different file.

The problem is that classes that depend on each other are not concatenated in the output JS in the right order.

For example, if I have class A that extends class B, it'd mean class B would have to be compiled before class A.

(1)

class A extends B { } //error - can't find B

class B { }

(2)

class B { }

class A extends B { } //works as expected

The problem is the file order in TypeScript compile is not defined according to class dependencies, resulting in many instances of (1).

It can be solved by manually defining the compile order with many lines of:

/// <reference path="myFile.ts"/>

however it is not ideal and can quickly become a headache in large projects.

The other option from what I read is to use external modules and be able to require/import relevant classes/files.

It sounds good, but it seems to only take care of ASYNC loading during runtime of the required files after each ts file has been compiled into its own js file.

What I need is defining the right compile order according to class dependencies during compile time from ts to js.

I googled "typescript compile order" and read thoroughly the first 10 results - meaning following references to turoials, documentations, videos, etc...

It seems people have been experiencing the same problem but their questions have never been answered to satisfaction.

From what I understand it should be possible to do using the CommonJS external module, but all I can understand from the answers is a general sense of what should be happening rather than a simple and straightforward answer of how to actually do it.

If you know the answer, let's solve this issue once and for all :)

4
  • If you want to compile it all into a single js file then I wouldn't use modules, but in this case you should be using references, and I also think that in large projects the references will help as they represents the relationships between the different files (if there are many files it can't get messy otherwise). It's not different than in other languages. With that said, have you tried specifing the files to compile using the files property in the tsconfig.json? Commented Jul 30, 2016 at 13:23
  • Specifying the files would just mean manually setting the compile order in a different way, correct me if I'm wrong. After taking a second look at using references, it is indeed similar to how it's done in other languages. Is there perhaps a more subtle expression than path based references with a cumbersome tag? Something similar to import with package based paths? i.e. something like "import Utils.Mathutils;" instead of "/// <reference path="../Utils/mathUtils.ts"/>" Commented Jul 30, 2016 at 15:39
  • You can use import (which is similar to what you're asking for) but that's using modules and it makes less sense in your case. I agree that the references are not very comfortable but that's probably all you have. Commented Jul 30, 2016 at 16:48
  • I understand. Feel free to send your response as a formal answer so I can mark it as the best answer - if you want. Thanks for the help. Commented Jul 30, 2016 at 18:24

2 Answers 2

1

It is not an issue with TypeScript, but with ECMAScript: classes are not hoisted.

See the book of Dr. Axel Rauschmayer:

Function declarations are hoisted: When entering a scope, the functions that are declared in it are immediately available – independently of where the declarations happen. That means that you can call a function that is declared later:

foo(); // works, because `foo` is hoisted

function foo() {}

In contrast, class declarations are not hoisted. Therefore, a class only > exists after execution reached its definition and it was evaluated. Accessing it beforehand leads to a ReferenceError:

new Foo(); // ReferenceError

class Foo {}

You have to take care to the order.

… or to use a bundler (Webpack) or a loader (SystemJS) with ES6 modules.

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

2 Comments

Yes, I'm aware of the reason it happens. This question is about compiling typescript in the right order so that classes will be evaluated before they are needed.
@RoyiBernthal I strongly advise you to use ES6 modules, for example with Webpack.
1

It is safe to say that there's no one way to do this. But I advise you to aim for the safest and most sophisticated build/bundling setup you can get. Depending on the scale of your project, consider the following points.

You can manually define a sort order for your output. Will it scale? No. Sorting hundreds of files and resolving conditional or circular dependencies can be a real pain, and erroneous by the way.

You can rely on reference tags and the compiler's ordering when using the out option. I think of this as the worst solution to aim for. At a certain project size and assuming there is a reasonable amount of non-perfect software design in it, you might end up with non-deterministic output ordering and zero warning or protection against dependency problems.

That is why I advise the following:

  • Use import/export to enforce your dependencies, you'll have compiled modules, and you'll need a loader/bundler, yes
  • Avoid circular dependencies, use the dependency graph or tools like madge to find them
  • Use a loader and a pleasent workflow to create your dist files (we use gulp/browserify/tsify/concat/...) - the core of it is about 20 lines of code

Is your project going to be tiny? 20 files? 50? Choose any. Is your project large enough? Modules it is. Enjoy several benefits like slicker node modules bundling or less need for name spacing.

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.