Use TypeScript as a truly optimising JavaScript Compiler (similar to google closure)

Topics: General, Language Specification
May 31, 2013 at 11:33 AM
From what I can tell, TypeScript appears to generate sometimes redundant code and it got me thinking. What I'd really like from this great compiler is to have it fully optimise my JS in a way similar to the google closure compiler.

Can it do this?

If not, it's missing a significant trick and it really should!
Coordinator
May 31, 2013 at 1:57 PM
With the current compiler, we're focusing on outputting JavaScript that has a high fidelity with the TypeScript, so you can get out what you put in. This lets you use TypeScript in a toolchain of your choice more easily. In the future, we may opt to add some optimizations to TypeScript, especially since we have the type system's information available to us then, but for now our goal is to keep a nice 1:1 with what you wrote.
May 31, 2013 at 2:05 PM

I'd like to see a compiler flag to output JsDoc comments based on TypeScript type annotations. Then the Closure compiler's type-aware optimizations could be applied.

May 31, 2013 at 2:12 PM
Rather than optimizing the JavaScript output, why not work with the IE team (and other browser vendors) to include execution hints in the code? This could include type hints to avoid boxing, hints for separate code generation for template specialization from a generic class/function etc...
Aug 21, 2013 at 11:42 AM
After the initial discovery curve, I don't really see a lot of benefit in maintaining 1:1 between JS and TS, particularly with the .map files. I would love to see some optimisation flags. Microsoft have always been good at optimising compilers, it seems a shame not to capitalise on that. People I talk to would also love to see an end to end solution for producing optimsed JS and TypeScript seems like the perfect candidate for this.

Once we got used to C, we stopped worrying about and looking at the ASM. Surely the same will be true of JS?
Aug 21, 2013 at 1:20 PM
I have to agree with @danieljsinclair. I don't see the benefit of maintaining a 1:1 between JS and TS. At the least, a compiler flag should be available to optimize the JS output. Anything released to production is minified anyways and while working in development or deploying development version of your scripts you have TS debugging via the generated map files.
Aug 21, 2013 at 3:35 PM
I've raised a feature request - please vote if you would like to see it!

https://typescript.codeplex.com/workitem/1542
Aug 21, 2013 at 3:39 PM
At least for a debug build the 1:1 with JavaScript needs to be maintained because we use that for debugging in the browser, because the .map files are too troublesome.

Ideally a release build should be capable of producing minified JavaScript.

I'm not sure what people mean by optimised code, because that is by default the job of the people writing the compiler.
Aug 21, 2013 at 3:49 PM
As I said at the least to have a compiler flag would be nice, so you could then use for example in a release build the optimization flag. If you are using web optimization and your scripts are bundled for release builds, then the TS outputted extends code could possibly exists multiple times in them same bundled script file. In a large project, say you have 20-30 different scripts bundled into a single script and what if you extend at least one class in every script. That would mean the same duplicate outputted JS extends code would exists 20-30 times in the bundled script. That's a lot extra bytes added to your script file.
Aug 21, 2013 at 3:50 PM
Edited Aug 21, 2013 at 3:51 PM
@nabog Ideally, yes. But that doesn't appear to be a current goal. For instance, if you write this in 0.9.1;
  function noop() {
  }
  noop();
you shouldn't get any code at all, but you get exactly that back again. An optimizing compiler would short-circuit that. SImilarly, an enum should result in a literal IMHO.
It's a contrived example, but the ability to rely on the compiler for that kind of optimization would be very liberating when writing higher level code.

See the Google Closure compiler for examples of function optimization. Make sure you select [x] Advanced optimizations with that same example.
Aug 21, 2013 at 4:35 PM
I think specific examples where the complied output can be improved is more likely to get the requisite attention.

As it stands this discussion is more along the lines of "hey, guys, why don't you try writing better code?!"

An important consideration (the reason I believe why the noop function cannot be optimised away) is that the compiler is only able to operate on one project at a time. What if there are other projects referencing that particular function?

e.g.

Project1
\foo1.ts
\foo2.ts
\foo3.ts     // function noop(){}
Project2
\bar1.ts
\bar2.ts
\bar3.ts       // noop();

Same story for the multiple "extends".

IMO this is sort of outside the scope of the compiler, and more in the realm of a bundling mechanism.

See also: https://typescript.codeplex.com/discussions/407063
Aug 21, 2013 at 5:14 PM
I think that because we have the Google Closure compiler which can easily be part of your build process, having the TypeScript compiler do the optimizations doesn't need to be a high priority. The TypeScript compiler could one day do a better job because it has the benefit of the typing information, but the tools we have currently are pretty good.

I would prefer to see compiler correctness improved and async/await support first. I think await is the potential productivity boost that takes TypeScript mainstream.
Aug 21, 2013 at 5:19 PM
I agree that TypeScript is good and correctness is import, but doesn't mean you disregard anything else that could make it better b/c async support is more desired. The point of this discussion to discuss TS compiler optimization.
Aug 21, 2013 at 5:23 PM
@nabog, if you have the below scenario...

Project1
\foo1.ts
\foo2.ts class foo2 extends foo1
\foo3.ts class foo3 extends foo1
\foo4.ts class foo4 extends foo1

You would get a unminified bundled JS output that contains this...

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;
d.prototype = new __();
};
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;
d.prototype = new __();
};
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;
d.prototype = new __();
};

Is it the bundler's responsibility to identify and decide what is duplicate code? Or should we have a compiler flag in TS that prevents code like this from being generated multiple times and maybe a startup/boot JS file that is created by the compiler (or developer) to hold any possible duplicate output code?
Aug 21, 2013 at 6:05 PM
@djarekg, the compiler could only do half the job, because the "extends" in the other projects would still be duplicated.

This why I think the bundler is in the best position to deal with removal of redundant code.

Minification on the other hand should ideally be carried out by the compiler, because it will be able to decide on the basis of access modifiers (public, private and hopefully, protected and internal in the future) on what should be minfied and what left alone.
Aug 21, 2013 at 6:59 PM
@djarekg I didn't say hey you stop talking about this. I certainly didn't mean it to come off that way. In my view an important part of discussing any feature is figuring out where it sits in the order of priorities. If the community asks for every feature equally loudly we're not sending a useful signal to the devs.
Aug 22, 2013 at 12:15 PM
I agree with @djarekg. Minification is quite different to optimization. We need both. And I don't think that TypeScript producing bundled/minified code is anywhere near as important as producing optimized code. Having said that, bundling to a single file might be something worthwhile if it produces cleaner optimizations.

Whilst I'd be happy for a post-build minification step, I don't agree with @Graijkowski that we can simply string together multiple different solutions like Google Closure over the back of TypeScript output, as this can be problematic, but if Microsoft were to at very least support integrating Google Closure end-to-end within Visual Studio and have the TypeScript compiler produce code compatible with Google Closure's advanced optimizer then if that was the 'supported' path, I'd be content in the interim. The trouble is, right now I don't think there is a well supported path. Furthermore, given Microsoft's experience in building optimizing compilers it would surely make sense to capitalize on the type-system available to TypeScript which becomes lost after compilation.
Aug 24, 2013 at 9:48 AM
danieljsinclair wrote:
...I don't agree with @Graijkowski that we can simply string together multiple different solutions like Google Closure over the back of TypeScript output, as this can be problematic...
You don't specify how tool-chaining is problematic. It's the norm for most builds.
Aug 27, 2013 at 1:39 AM
Here's the no-op case, with some more surrounding code:
debugger;

doSomethingElse(doSomething(window)); 

function noop(){
}
noop();

function doSomething(w)
{
   w.noop = function() { alert('I want this function to alert.'); }
   return w;
}
function doSomethingElse(z)
{
   z.noop = function() { alert('I am a no-op') }
}
I could make it even worse by doing something like:
var q = 'noop';
function doSomethingElse(z)
{
   var r = getFunction();
   z[q] = r;
}
function getFunction()
{
   return function() { alert('I am a no-op') }
}
I don't think that it can safely assume noop is empty at the call, I don't think there's a way to be certain that it's safe to remove that call, at that spot. Even a call to new can change what the function is pointed at, and so I think you have to output the function call no matter what. That'll be the case for anything involving a function or a var, including built-in vars.
Aug 27, 2013 at 3:56 AM
@dbacher
For sure there are limits to what function calls and variable references can be detected with a reasonable amount of effort. In situations like the one you describe it is necessary to hint the compiler that a given function is not redundant. See https://developers.google.com/closure/compiler/docs/api-tutorial3#removal

@markrendle
In regards to tool chaining: I found that source maps can be problematic - last time I checked only view tools supported rewriting source maps so that the final code will still reference the original source input from the beginning of the tool chain.
Aug 27, 2013 at 12:57 PM
@mistaecko I'm using Uglify2 and it accepts an input source map to let it output a second source map that still points to the original ts files. Seems to work OK in Chrome Dev Tools. Don't know if Closure supports the same thing.
Aug 27, 2013 at 3:28 PM
nice. I will check it out. Thx @markrendle