Suggestion: minification

TypeScript should support emitting minified JavaScript.
There are several different things we could support:

Just remove whitespace
Minify unobservable identifiers
Remove provably dead code
Whole-program minification (i.e. closure compiler)
(Others?)

If option A is fully efficient, I don't see why you would need B or C!
I would love to have option A's --minify-advanced!

I think the issue is that the compiler shouldn't necessarily be responsible for minification. I would much rather see all the information that TypeScript knows be emitted as jsdoc comments. That way JavaScript tools can utilize that information without needing to have a TypeScript parser.

Don't compilers generally have a switch to optimize for file size?  Why not this one?

Compilers don't usually have a goal of near 1:1 mapping with source.
The point is that generating the resulting code is completely different with optimizing for file size (and other optimizations) than what it currently produces. I'd suggest a separate tool is created for an optimizing compiler (one that optimizes for either file size or performance), that makes use of the API tsc offers. I was planning on experimenting with the tsc compiler API so I may do an experiment in producing minified output.

The goal of the TypeScript compiler could be near 1:1 mapping of source unless the /min switch is set, in which case the goal would be to minimize it to the hilt without changing its exports.

Yes that's true, but then the back-ends are basically completely separate. I'd recommend implementing it as a separate module/project that uses the API, then if it does prove very useful it can be evaluated to be merged as part of the tsc command line.

Meta: Is this the canonical issue tracking the proposal to create a path to an optimizing backend (be it built-in or via Closure annotations)? It appears to be at a glance, but if I'm missing a more appropriate spot, I'd appreciate a pointer to it.
I'd just like to throw in my 2¢ in support of an optimizing backend. My company's using Typescript very heavily, and while using Closure's basic optimization mode on the output helps to a certain extent, it's quite obvious that we can do a lot better. Code size may not matter to everyone, but it should -- it's not just about over-the-wire size, which is (mostly) mitigated by gzip, but about memory use, parse time, and runtime performance (yes, you can still achieve significant improvement through static optimization). There's a reason that Google invested heavily in optimization for both the Closure and GWT stacks (full disclosure -- I worked a lot on GWT and to a limited extent on Closure when I was there).
Just to be clear, this is what I mean when I suggest that we can do a lot better than just dropping whitespace and obfuscating local identifiers:
function rAb(a,b){if(!b.ib.a){Sae(a.a.a.a.a.d,(Klf(),new Nwf(b)));bOe(a.a.a.a.a.i,a.a.a.a.a.i.c)}}
function Cqb(a,b){a.f=b;if(!b.yb){a.a.we(new Aqb(a.b,a.f,null));return}wqb(a.d,b.Fb,new Iqb(a))}
function Bqb(a,b){var c;a.b=b;c=b.g;if(c.fh()){Cqb(a,b);return}vae(a.c,c.Br(0).Fb,false,new bbe(new Gqb(a)))}
function Pfd(a,b,c){var d;d=new Ufd(a);Bgd(a.a,d);Agd(a.a,b,c);dgd(a.b,new Wfd(a));Jpb(a.b,new Yfd(a));Qfd(a)}
function Ppd(a){var b;b=new qid(Dte(AFd(a.a)),bue(AFd(a.a)),aue(AFd(a.a)),Opd(a),knc(XEd(a.a)));return b}
function $pd(a){var b;b=new kmd(NXf(QFd(a.a)),(new rnd,KFd(a.a),new ndf),new hid(pKc($Ed(a.a))));return b}
function Rnd(a){var b;Sgf(a.s);if(a.o.S){return null}b=a.U.vg();return !!kfi(b).length&&!u_(b)?b:null}
function Pnd(a){Sgf(a.s);if(a.o.S){return nt(),mt}if(a.N.e.f.a){return nt(),mt}else{Sgf(a.M.e.f.a);return nt(),kt}}
function Gld(a){if(!a.j||!ZM(a.d.b,(Jw(),Qv))){return scf((Yci(),Yci(),Wci))}return icf(a.n,new Jld)}
function Mkd(a){a.a.wk((RJf(),KJf));wbb(a.a,eui);a.a.Oe(true);Fbb(a.a,new Ukd(a),BRf?BRf:(BRf=new wQf))}
function Jhd(a,b){var c,d;d=Bae(a.c,b);c=Lld(Ypd(a.a.a),b,d);return Nhd(new Ohd(a),(Ngf(c.d),new Ild(c,true)))}

The above is a random chunk of GWT output from a Google app. The details don't matter -- the point being that aggressive optimization can dramatically reduce output size, make a non-trivial difference in parse time and runtime performance, and even amplify gzip compression by allocating identifiers in such a way as to reduce input entropy.
As has been pointed out earlier on this thread, there are two obvious routes we can take:

Build an optimizing backend into tsc.
Add an option for generating Closure jsdoc annotations.

I don't have a strong preference -- as long as it's possible to get good output, I don't care much how we get there. The output example above came from the GWT compiler, but Closure achieves similar results. Michael Bolin (who did a lot of the work on Closure at Google) created a proof-of-concept (http://bolinfest.com/typescript/) a couple of years ago, but didn't take it much further than that. It's not an entirely trivial exercise, because of the impedance mismatch between Typescript and Closure's inheritance mechanism in particular, but it doesn't seem like brain surgery either.
The hardest design problem, as I see it, is dealing with exposed vs.  optimizable symbols. Closure has annotations for dealing with "exporting" symbols, and Typescript would need something similar to make the optimizer useful. There are also important edge cases like dealing with externally defined objects (both importing third-party libraries, and dealing with the output of APIs like JSON.parse). The compiler must know about these things if it is to avoid breaking the output with an aggressive optimizer.
I think it would be fairly easy to rally a few people to work on an optimizing backend for Typescript, but only if the team is bought into the idea. Trying to bolt such a thing onto the existing compiler, without the ability to tweak the compiler and language a bit, is probably a fool's errand. So I'd greatly appreciate any indication from the team as to their disposition on the subject.

I really like the "random chunk of GWT output from a Google app". In addition to the benefits mentioned above, another objective is protection of intellectual property through obfuscation. If anybody has tried stepping through the code in Google Maps then they will know the protection that aggressive minification can provide in this regard.
The annotation (for excluding minificaton) is not only relevant for externally defined objects, but also relevant when properties of JavaScript objects are bound to elements in the HTML.

I decided not to say anything about obfuscation for the purpose of, well... "obfuscation", at least partially because I know that tends to raise a lot of hackles in the web world :) A bit more seriously, I'm less concerned about it simply because I've spent so much time reverse-engineering highly-obfuscated Javascript (e.g., http://www.j15r.com/blog/2005/02/09/Mapping_Google) that obfuscation only feels like a speed bump. But hey, a big speed bump can still be useful.
Regarding annotation, right -- I forgot to mention that runtime-provided types are equivalent to external code. The (small, but important) distinction being that you tend to have relatively stable and available IDL for the former, whereas the latter's a bit of the wild west. If you have type information (e.g., via DefinitelyTyped), you're good to go, but if not you have a problem. Personally, I'd be fine with requiring explicit type information for all external libraries as a precondition for aggressive optimization, but I'm not sure that's a majority opinion.
FWIW, Closure basically takes that approach. If you don't have type annotations for external code, Turing only knows what it will do under advanced optimization (actually, I don't even think Turing knows, because it's probably undecidable, and the compiler certainly can't tell you). With GWT it was a bit easier, because you needed explicit (lightweight, optimized away) Java interface wrappers to call into Javascript in the first place. So the compiler only has an aggressive mode, because it can prove that the output won't be broken (as long as your Javascript methods don't "cheat").
Requiring explicit types also works for dealing with parsed JSON (our rather large body of Typescript uses interfaces for all parsed values; otherwise we'd be breaking things left and right). I believe Closure allows for this, but the traditional method was to require string accessor syntax for any properties that weren't defined by the compiler (e.g., value = parsedThing["someField"]). This is a pretty foul hack that I believe goes unchecked by the compiler, though, and I wouldn't be in favor of doing anything that nasty in Typescript. After all, that's one of the great benefits of making interface definitions an order-of-magnitude less verbose than in Closure.

@joelgwebber that is a really great appeal.
Just wondering: with variables and module/class fields being necessarily declared, can't the optimizer ONLY rename members which are traceable back to a declaration?  An any or an any which is known to implement an interface is still external code and those identifiers are not declared.
But in code with multiple namespaces, we would be forced to export too many items - items which are needed within the application by different namespaces, but not outside of it.  So there'd need to be some additional level of accessibility between non-export and export, like internal?

Don't forget there should be available an option in Visual Studio --> Config Transform (transformation) for Web.Config that are tied in to Visual Studio's Configuration Manager.  That way we as developers should not be seeing minified javascript during development, especially when there's error or when debugging javascript source code.
I know this is about TypeScript minification thing but when in Visual Studio runtime via F5 key, it compile TypeScript to Javascript & I wouldn't want to see minified Javascript code during developing.  Also, the Config Transform are tied in to publisher too.   I would rather have minified javascript code when publishing to production sever but not minified to developer server.
Just saying.

@Gitgiddy : I believe that's correct. The compiler can't go renaming symbols whose provenance are not known, and which it can't prove haven't "escaped" into any references or external code. In a language like Typescript, this requires global type inference. I know the compiler does a certain amount of type inference already, but I don't know how broadly scoped it is -- if it's just for locals, that could prove problematic.
@fletchsod-developer: Of course. We'd be nuts to run an optimizing backend with global type inference on every compile. You really only want that before deploying to production.

I really would love to see this issue moved forward. It's evident that this is something people want (few users of Typescript would object, I'd challenge anyone to find a die hard objector). The issue is, it needs to be implemented correctly.
@Gitgiddy, I think you're absolutely right about the need to declare modules as internal vs external. But to add a keyword to the language for the sole purpose of minification isn't compelling. Private, protected, public - all of these keywords are very helpful to us for purposes other than minification, though they're incredibly helpful for that purpose.
With the merger of Typescript and AtScript, we finally have a syntax that's endorsed for data annotations. Is there a roadmap for specifying which annotations can be picked up by the compiler, I wonder? I'm sure that's something that's wanted by many people, so I'll assume that's avenue to pursue on a configuration level.
What if we were to steer this thread to a conversation about some standards which could be implanted, should minification be a feature of the compiler?
To start -

is it safe to assume anyone desiring a higher level of minification than UglifyJS will be willing to put the work into their codebase, as to not use implicit any, and have every piece of data defined by an interface?

Is there a real objection to NOT minifying private and protected members?

Can annotations be used to provide directives to the minifier?

Solving these questions would be a good start. I feel that there could be more taken though:

If annotations cannot be used on Enums, interfaces, or modules (as I don't see that in the AtScript syntax) how can we provide information about the intended minification level for these features? Can the annotation system be extended to work on things other than classes?

Don't get distracted by the last point there. Let's start with the first three? I feel like there could be some progress on something if a common consensus began to emerge.

Perhaps with Ron Logan's (aka ronlo http://ajaxmin.codeplex.com/team/view) expertise, the development of this feature can elevate? He has done tremendous work in JavaScript minification over the past several years.

I think main selling point for dead code elimination is util libraries like lodash/underscore. TypeScript would become immediately much better for compiling those, than e.g. modularity of lodash which requires programmer to keep the mini-packages updated.

I think this is a must have for TypeScript.  It's a great language and gaining a lot of support, especially with the Angular 2.0 stuff.  Now that I have developed a lot of TypeScript code and almost ready to deploy I now wish I hadn't used such long and descriptive naming (which I always do).  Unfortunately, I thought they would minify OK.  I tried using Closure but it broke more than it helped.
Coming from C# background I think annotations are the way to go.  But they should be "opt out" instead of "opt in" for class properties and methods (both private and public), therefore they are minified by default.
Is there a way to elevate this so that someone on the TS team can at least tell us if it is being considered in the near future?
Until then I'm going to try to roll something on my own (which will probably leave me bruised).

Is there a way to elevate this so that someone on the TS team can at least tell us if it is being considered in the near future?

Minification has always been one of our most requested features, and one that we have talked about on and off throughout the past few years. It is a substantial amount of work though, assuming you are not just looking for white space removal, and variable name shortening. Moreover, minification has existed in JavaScript tooling for a long time, and there are already multiple minifiers that plug into existing JS workflows. We have been really busy with ES6 and other language/tooling features for TypeScript which made us shy from taking on such a big problem; specially that TypeScript compiles to JavaScript which allows you to leverage any existing JS tools out there.
That said, it is not that we are ignoring the issue; it is still open because we believe that there is problem out there that TypeScript can help solve.

@mhegazy, thanks a lot for the reply.  I'm sure you're very busy and I wasn't trying to push!  I just wanted to know if anything was on the near term radar.  Good luck with the ES6 stuff.  TypeScript is great.

I second @jasonwilliams200OK's suggestion regarding Ron Logan. They have a well-maintained minifier (that I have used in the past) that could possibly be adapted to work for TypeScript. I have found Ron very proactive on the subject.

We are currently doing some JSDoc infrastructure work including parsing and AST representation, once that is done, generating type-annotated JSDoc comments should be fairly easy and will unblock more minification/optimization scenarios. We would be open to taking a PR for that.

Would like to see TypeScript compiler to support advanced minification feature!
So far, I have to create the externs file by myself and use the bracket notion to export the symbols.
For now, I am trying to generate the extern files from the .d.ts type file (aforementioned as Option B by @NoelAbrahams) by using typescript compiler APIs, so I don't need to manually change the externs file every time I use a new external API.

Why has this issue been closed?

@antoinerousseau, this issue is not closed :question::grey_exclamation:

oops yes sorry I got confused with the Closed label next to the #3503 reference just above

@mhegazy, you mentioned that it would be difficult to do, assuming that you weren't just looking for white space removal, and variable name shortening.
In my opinion, it is the second item in your list which would be the most beneficial. In our pipeline, we use UglifyJS to handle minification of TypeScript output, and it handles the AST optimization pretty well. What it can't handle, that Typescript can handle, is the second item you listed as a simpler task (variable name shortening.)
How could someone get more involved in that area of the project? Introducing variable name shortening as an option, would in my opinion be a huge piece of the pie, as that's the area where type definitions are required the most.

@bryanerayner we are currently looking into updating the implementation the emitter to allow for a transformation pipeline. no ETA at the point, but when this happens emitter changes should be much simpler to do.
I worry that a set of options to partially minify will not solve the original issue, but will introduce new complexities for users and in the code base. if this is something that you find interesting, feel free to look at the compiler sources and the API, and provide a proposal for how you intend it to work.

@mhegazy, so theoretically speaking, given the compiler is now exposed for third-party/external extensibilities, we can get a hook into AST and then implement the ultimate minification possible with all the trans-compilation context info. In this case, minification should be a feature of tsc, so consumers of TS should not rely on the external module (uglifyjs etc.).

@jasonwilliams200OK i agree. i believe the request here is for tsc to subsume your minifier in the tool chain. i do not think you would want to run tsc --minify then run your minifier again.

++

I do not understand why those things in ts 1.7 roadmap can be more important than minify. I had no choice but shipped my code with full function names. It is as unprofessional as selling apples with apple tree attached, selling house without  scaffold staging removed, taking your girlfriend to motel with her mom in the trip.

I do not understand why those things in ts 1.7 roadmap can be more important than minify.

Whaaaat? "Colorization of JSX code in VS 2015" (https://github.com/Microsoft/TypeScript/issues/4835) is NOT important than minification? :stuck_out_tongue_winking_eye:
Agree minification deserves some love. Not only shortening of names and elimination of whitespace but also some deep optimizations, joining of scattered file content etc. too. But naturally this is a complicated task which can be carried out in phases; basic->advanced.

Because TypeScript emits idiomatic, valid JavaScript, I suspect it isn't a priority because there are plenty of other tooling out there that does better than likely what the TypeScript can hope to achieve in the short term.  Honestly folks, if you depend on TypeScript for you minification, you are likely not going to do an effective job of it.  There is the Closure Compiler and UglifyJS.  You can have those today, actually you could have had them before TypeScript.

@kitsonk, there are some problems with using closure and uglify that have been discussed above.

Maybe I'm missing something, but I've been having significant difficulty in getting sourcemaps and uglify/minify to work as expected.   What I really want is a TSC.exe compiler option to minify the file with sourcemaps.   IMO, simply stated, if you are using AMD or any other module based code, you really don't care about how the resultant JS looks because you have the sourcemaps.
Here's what I've found (spend hours and hours on this):

tsc with sourcemaps works perfectly without minification/uglify.
gulp-typescript + gulp-sourcemaps generates different sourcemaps than 1)
Any attempt at rendering minified files externally using gulp-sourcemaps, say in a 'min' folder creates strange references that don't resolve without overriding the sourceRoot with a function.

Ultimately, the above basically says there are bugs in the gulp plugins, but I would be happy if simply my resultant JS files were minified straight out of the TypeScript compiler.

Also, it would be neat if you could replace the __extends code with an AMD module called 'extends' or something. :)  Could take a chunk out of the total code.

@mhegazy Is there any way with 1.7 to control or get access to the text writer or emitter? I just want to do a bit of experimenting with whitespace removal and private identifier mangling.

For anyone who is interested.. I've experimented with walking the container nodes and minimizing blockscopedvariables. This requires a minimal amount of code, so I'm going to add it to TsProject. It will work only on bundles.
As for whitespace elimination, there is currently no way to change the way the indentation works in the js text writer, so this will have to be done as a processing step after the bundle emit.

Fantastic! That's a great first step.
On Sat, Dec 12, 2015, 5:08 PM Todd Thomson<EMAIL_ADDRESS>wrote:

For anyone who is interested.. I've experimented with walking the
container nodes and minimizing blockscopedvariables. This requires a
minimal amount of code, so I'm going to add it to TsProject. It will work
only on bundles.
As for whitespace elimination, there is currently no way to change the way
the indentation works in the js text writer, so this will have to be done
as a processing step after the bundle emit.
—
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/8#issuecomment-164200832.

Can someone please clarify if minification of the TypeScripts output is likely to be problematic? Some of the files in a project I'm working on seem to minify correctly while others do not.

@richwatts, generally it should not be problematic but sometimes the configuring uglify is bit unpalatable. I recently had issues with jQuery and some other (non-AMD) plugins in the mix. I was using gruntfile.js to produce single minified file. Here is the how the configuration looks like:
(gruntfile.js in the root of simple 'website' project)
/// <binding ProjectOpened=uglify/>

// the above line is produced & parsed by VS2015's Task Runner Explorer for
// binding the grunt/node.js task with EnvDTE events.

module.exports = function (grunt) {
  require('load-grunt-tasks')(grunt);
  var jsBinBase = 'assets/bin/';

  grunt.initConfig({
    uglify: {
      options: {
        compress: true,
        mangle: {
          except: ["$super"],
          toplevel: true
         },
        sourceMap: true
       },
       target: {
         src: [ // mixed sources, AMD and non-AMD
           'assets/js/rickshaw/vendor/d3.v3.js',  // needs to be in order
           'assets/js/jquery-2.1.1.js', // needs to be in order
           'assets/js/jquery-ui/jquery-ui.js',  // needs to be in order
           'assets/js/**/*.js', // include all js files generated by tsc or otherwise
           '!assets/bin/**'  // this is exclusion
         ],
         dest: jsBinBase + 'app.min.js'
       }
     }
  });

  grunt.registerTask('default', ['uglify']);
};

(package.json side by side with grunt.js)
{
   "version": "0.0.1",
   "name": "Dummy",
   "private": true,
   "devDependencies": {
     "grunt": "^0.4.5",
     "grunt-contrib-uglify": "^0.11.0",
     "load-grunt-tasks": "^3.3.0"
  }
}

As you can see, i had to hand pick some dependencies in targets to make it work. But in case you are using everything-AMD, you wouldn't need to care about the targets order.
If you have similar setup, you can generate the combined output from tsc with source maps and then in the above grunt example change sourceMap: true to sourceMap: 'path/to/your-output.js.map'. With that, uglify will generate mapping between min file and the actual ts file (not just min.js to js), which comes handy when debugging with browser dev tools.

@richwatts, uglify will not minify private methods.

@NoelAbrahams,

Private visibility is a design-time construct; it is enforced during static type checking but does not imply any runtime enforcement. ... TypeScript enforces encapsulation of implementation in classes at design time (by restricting use of private members), but cannot enforce encapsulation at runtime because all object properties are accessible at runtime. Future versions of JavaScript may provide private names which would enable runtime enforcement of private members
He (@richwatts) is not uglifying a "typescript file" but the "typescript output", which is JavaScript file..

@jasonwilliams200OK, well yes. I think most people on this forum understand private methods without recourse to a quote.

The point about private methods is, given the following TypeScript
class Foo {

	public myPublicMethod(){
		this.myPrivateMethod();
	}

	private myPrivateMethod(){
	}
}

We get JavaScript output:
var Foo = (function () {
    function Foo() {
    }
    Foo.prototype.myPublicMethod = function () {
        this.myPrivateMethod();
    };
    Foo.prototype.myPrivateMethod = function () {
    };
    return Foo;
})();

Running the JavaScript output through uglify produces:
var Foo=function(){function t(){}
return t.prototype.myPublicMethod=function(){
this.myPrivateMethod()},
t.prototype.myPrivateMethod=function(){},t}();

This minification is inefficient, because the private method should have been reduced to a single character, say a instead of myPrivateMethod. Doing so would improve minification by 4% for this simple example. Unfortunately, uglify cannot do anything about it (without further annotation) because the TypeScript compiler emits both the private and public methods on the prototype, so that there is no distinguishing between the two.
Those writing JavaScript libraries would typically write their code in such a way as to obtain better minification, for example:
    function Foo() {
    }
    Foo.prototype.myPublicMethod = function () {
        myPrivateMethod.call(this);
    };

    function myPrivateMethod() {
    };

So, while using uglify is not "problematic" as such, it will not be as efficient as minification generated by a system that understands private methods, that is, minification generated directly by TypeScript.

^^ yes! Exactly. :)

@NoelAbrahams, while I agree that TypeScript has better context to improve the compression, the private methods issue you are describing is non-existent with UglifyJS if you use the correct switches:
npm install uglify-js

:: with --mangle-props
.\node_modules\.bin\uglifyjs --mangle --mangle-props -- yourexample.js
var Foo=function(){function o(){}o.prototype.a=function(){this.b()};o.prototype.b=function(){};return o}();
:: ^107 chars

:: without --mangle-props
.\node_modules\.bin\uglifyjs --mangle -- yourexample.js
var Foo=function(){function t(){}t.prototype.myPublicMethod=function(){this.myPrivateMethod()};t.prototype.myPrivateMethod=function(){};return t}();
:: ^148 chars

but, don't you want publicMethod not to be mangled?

@eggers, then you use  --mangle-regex="/Private/". As a library author, if you are using some convention like underscrore before/after the private properties names, then this option for you.

Which is why it is best left to tools that are designed to minify.  While TypeScript might have more context, there has been a lot put into the minfication tools to make them work in the myriad of use cases.  The only improvement I can really see is that it "intelligent" minification is built into a build pipeline that utilises the language services of tsc to provide some guidance.  Integrating that directly into TypeScript though seems to be an anti-design goal:

Provide an end-to-end build pipeline. Instead, make the system extensible so that external tools can use the compiler for more complex build workflows.

as well as:

Aggressively optimize the runtime performance of programs. Instead, emit idiomatic JavaScript code that plays well with the performance characteristics of runtime platforms.

Which is why I guess I am still surprised @RyanCavanaugh is suggesting it.

@kitsonk, I agree.
postcss is an AST for CSS. Any CSS utility (compiler, compressor, linter) which implements postcss gives the free advantage of integrating all the other utilities built on top of postcss via piping. This integration is not "just" pipe contract either; they can share the context (the actual AST!) with each other.
I wonder if there is any general purpose AST built for JavaScript, which TypeScript, CoffeeScript, Uglify, SweetJS, LiveScript, JSHint, JSCS etc. can get compliant with to be able to "share the context" with each other and yet provide legendary experience -- as opposed to each implementing redundant features in different ways which BTW also goes against the DRY and single responsibility principles.

Unfortunately, this discussion has been sidetracked by spurious arguments (by those, I suspect, not really familiar with real-world minification problems).
As I said above

uglify cannot [minify private methods] (without further annotation)

Without asking owners of code-bases spanning tens of thousands of LOC to carry out special annotation, there is no tool in the present evironment that can effectively minify TypeScript generated code.
A number of real-world problems have been discussed above, such as dealing with properties that are bound to the DOM (which shouldn't be minified). Please do take the time to read through the comments.
We are at present facing a script that is approaching many hundreds of MB and this feature suggestion remains critical for us.

I am well familiar with real-world minification problems. I am simply explaining what is possible with Uglify today.

Please do take the time to read through the comments.

Have you read mine? I am not against this proposal.
Please speak for yourself only and don't be sarcastic. So we are sharing thoughts here and this conversation is well on topic (Uglify is relevant).

@jasonwilliams200OK, sorry, no offence intended. I do urge you to stop making impractical suggestions, though :smile:.

Unfortunately, this discussion has been sidetracked by spurious arguments (by those, I suspect, not really familiar with real-world minification problems).

That was a bit rude and uncalled for, and rather ill-informed.
I am very familiar with real-world minification challenges.  Also you stated:

It is therefore likely that many library developers are put off from using TypeScript because of these inefficiencies.

I can tell you, as a CTO of a major company behind one of the largest open source JavaScript libraries hasn't dissuaded us from TypeScript.  Maybe you should do a bit of research before you toss out throw away comments like that.
  How does that become?

I do urge you to stop making impractical suggestions, though

Even with a smiley, that is uncalled for...

Congratulations on your job.
The first suggestion was impractical, because it also minifies public methods. The second suggestion is intrusive, because it involves annotating private methods and/or following some sort of convention, such as an underscore prefix.

You are seeing it as intrusive, I take it as room for improvement in Uglify, on which many users rely today. Turned out I am not alone as I have found this: https://github.com/mishoo/UglifyJS2/issues/103. See the comments by @jrhite. It is possible to control what properties to mangle in uglify today with programmatic usage, though further improvements might be required as the issue is still opened. TypeScript team can still take dependency on UglfiyJS in implementing this feature by injecting the context and controlling the internal steps.
Like i mentioned earlier, if TypeScript  support some common AST core with foreign interface like postcss provides for CSS (so you can plug/swap minifier of your choice or any utility) and LLVM provides for native langs, then these features can be provided more effectively in a modular and extensible manner.
Please do implement this feature, preferably by engaging Uglify.
To sum up, we can achieve the same goal with one of the following approaches:

Take hard dependency on Uglify. Less flexible, simplest to implement and simple to use.
Provide consumer to control annotation in generated code so they can make choice on taking dependency on Uglify or something else. Conversely, typescript to always annotate the private methods with fixed annotation. Flexible enough, simple to implement and use.
Be the first to provide intermediate AST core tier for convergence such as postcss. Very flexible and extensible, hard part is to convince everyone to converge to a common interface.
Implement the minification feature from scratch. This will take couple of iterations to become as good as Uglify, but eventually it will work too.

@NoelAbrahams, no problem. Same goal, different approaches. Like i said earlier, we are sharing thoughts and I provided information on what can be achieved today. Final decision on how will come from TypeScript team.

@jasonwilliams200OK  - I have no skin in this, but just an update. mishoo/UglifyJS2#103 should be closed as full support is already there. Additionally, another feature was added which allows very useful control over which properties do and don't get mangled. Check the uglify2 docs for more info.
--mangle-regex                Only mangle property names matching the regex
Cheers!

I know everyone's looking for the best direction for the project, so let's keep that in mind and try to keep the discussion civil and driven by the issue at hand.

@jasonwilliams200OK,
I think your suggestion of TypeScript producing JavaScript output annotated in a language that Uglify (and perhaps other minifiers) can understand is probably the easiest route forward at present. This would work for us as we already use Uglify for our minification.

Which is why I guess I am still surprised @RyanCavanaugh is suggesting it.

@kitsonk TypeScript issues 1 through 22 were manually ported by me from the old CodePlex site based on number of votes they had at the time. I don't really remember why we picked that number.
Honestly for all the comments that have been logged here, I'm shocked that there doesn't seem be be a reference implementation based on some very straightforward usage of the language service.

@RyanCavanaugh,

Honestly for all the comments that have been logged here, I'm shocked that there doesn't seem be be a reference implementation based on some very straightforward usage of the language service.

If the TypeScript team is not going to implement this feature then I suggest this issue be closed. That may motivate someone to take this up. It would also help us in the decision to consider looking for alternatives.
This was one of the earliest issues raised (7th of October 2012 - just a few days after TypeScript went public), received a large number of votes before being moved to GitHub (6th in terms of number of votes), and three years later there has been no resolution.

@RyanCavanaugh,

Honestly for all the comments that have been logged here, I'm shocked that there doesn't seem be be a reference implementation based on some very straightforward usage of the language service.

If the TypeScript team is not going to implement this feature then I suggest this issue be closed. That may motivate someone to take this up. It would also help us in the decision to consider looking for alternatives.
This was one of the earliest issues raised (7th of October 2012 - just a few days after TypeScript went public), received a large number of votes before being moved to GitHub (6th in terms of number of votes), and three years later there has been no resolution.

We haven't ruled it out. If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly and I'm inclined to not proceed with it.
This has been sitting in "Needs Proposal" because, as I've outlined here many times, we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers, in a way that is general-purpose enough to work for most people.
Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways. I don't think it would be a great trade-off for us to invest engineer-months in a minifier that is 3% better than what's sitting on NPM today in exchange for not having a smarter type system or not having better editor support or not having the compiler be 15% faster.
If someone put up a fork with a prototype minification algorithm that produced substantial minification gains over the competition, we'd find that very convincing. If no one is bothering, that really is a sign that while everyone might enjoy the pleasant convenience of built-in minification, it's not a area where we can deliver a lot of value.

We haven't ruled it out. If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly and I'm inclined to not proceed with it.
This has been sitting in "Needs Proposal" because, as I've outlined here many times, we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers, in a way that is general-purpose enough to work for most people.
Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways. I don't think it would be a great trade-off for us to invest engineer-months in a minifier that is 3% better than what's sitting on NPM today in exchange for not having a smarter type system or not having better editor support or not having the compiler be 15% faster.
If someone put up a fork with a prototype minification algorithm that produced substantial minification gains over the competition, we'd find that very convincing. If no one is bothering, that really is a sign that while everyone might enjoy the pleasant convenience of built-in minification, it's not a area where we can deliver a lot of value.

If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly

In our own case, this was initially not a big concern, because the code-base was smaller. But as we deal with more feature requests, each accompanied by growth in code, this is becoming critical.

If no one is bothering

My point is that so long as this feature is open, the expectation is that TypeScirpt will do something about it.

we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers,

I agree that something more than simply minification of private methods would be required for TS to implement minification entirely by themselves. But providing output that Uglify can understand might be a start. I might have a go at providing more justification, but can't promise anything at the moment due to work commitments.

Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways

Not sure how "a reference implementation based on some very straightforward usage of the language service" suddenly turned into a "huge amout of work"  :smiley:

If someone has waited three years without looking for an alternative, they apparently don't need minification all that badly

In our own case, this was initially not a big concern, because the code-base was smaller. But as we deal with more feature requests, each accompanied by growth in code, this is becoming critical.

If no one is bothering

My point is that so long as this feature is open, the expectation is that TypeScirpt will do something about it.

we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers,

I agree that something more than simply minification of private methods would be required for TS to implement minification entirely by themselves. But providing output that Uglify can understand might be a start. I might have a go at providing more justification, but can't promise anything at the moment due to work commitments.

Our current belief is that this might be possible, but is almost certainly a huge amount of work that we could spend in more productive ways

Not sure how "a reference implementation based on some very straightforward usage of the language service" suddenly turned into a "huge amout of work"  :smiley:

With a transform based compiler (#5595) this would probably be a lot easier to implement in the compiler or using an external project.

With a transform based compiler (#5595) this would probably be a lot easier to implement in the compiler or using an external project.

[apologies for the wall of text]
I think Ryan's suggestion that the state "NeedsProposal" means "it's not yet been made clear how to do this in a way that's a clear win over alternatives" is perfectly reasonable. There does need to be some way to express that concept.
I can speak a little to whether the current state of affairs is "good enough". Our current deployment process produces two output scripts, one of which is fairly small (~45k uncompressed) but highly size-sensitive because it's loaded in customers' pages, the other being larger (~500k uncompressed) and growing rapidly as we add features. These are produced via tsc --out [...] and closure --compilation_level=SIMPLE_OPTIMIZATIONS. I've tested various options through uglifyjs and gotten similar output, so I think we can safely assume this is close to the best we can do without type-aware optimization. This is "good enough" for the time being, but it's going to become a problem eventually. The issues we're likely to run into are download time over slow networks; but more importantly, parse time on mobile devices. The latter can be significant because parsing blocks the UI thread, and happens every time the script's evaluated (i.e., the parsed output is in-memory only, and thus not cached).
Eyeballing the final output and comparing it to the GWT output I pasted above (or any product run through either GWT or closure with "advanced" optimizations), I'd be willing to bet that we could cut these files by about 40% pre-gzipping. That's a bit of a wild-assed guess, but with the enormous number of unmangled identifiers in the output, along with the lack of inlining and other relatively simple optimizations (c.f. https://www.fullstory.com/s/fs.js), I doubt it's far off. And that kind of improvement would give us a lot more headroom for future development.
So, what's required to get this level of optimization? Reliable type information. If you look at the approach that tools like uglifyjs uses, e.g. to mangle property names, you'll see that it's kind of a hack (which is the best you can do without type information) -- you have to give it a list or pattern of identifiers that are "safe" to rename, and if you miss anything the output will be broken in unpredictable ways. OTOH, if you look at either the GWT or Closure (in "advanced" mode) compilers, you'll see that they carefully track the types of all expressions. This allows them to know not just which identifiers are private and can be renamed, but also which are external to the program (e.g., if I write window.ExposedThing = internalFunction, I need to know both that I can mangle internalFunction, and not ExposedThing).
I've considered digging into tsc to see whether it's feasible to do something similar, but have put it off both because of time constraints, and because I'm not certain whether it's compatible with the team's goals. My biggest concern is that there seems to be more of a focus on single-file compilation and working with the various --module options, than there is on monolithic compilation using ///<reference> directives. But producing globally-optimized output conflicts to a certain degree with single-file compilation. If anyone on the team could offer some insight into whether continuing to improve monolithic compilation is a goal, that would help me decide whether to devote time to the problem (and recruit others I know using Typescript to contribute to optimizations).

It's probably worth roughly sketching out what I think could work in the context of Typescript:
Because the language allows untyped expressions, and that they're fairly common, it's likely unrealistic to take quite the same approach as when compiling a much stricter source language like Java. Because the GWT compiler can rely on knowing the precise (and generally singular or very small) set of types for each expression, it can be extremely aggressive in renaming identifiers and allocating those names. Referring to the output fragment I pasted above, you'll see lots of things like:
function abc(a,b) { if (a.a == b.a) {b.b(a, b); }
// ...

Outside of external references, you'll see essentially no human-readable identifiers, and the names are mostly linearly allocated in small scopes (so you get lots of a, b, ..., aa, ab, etc). If you're careful about sorting the output, you can even get significant improvements in gzip performance by reducing entropy within the compression window.
With less-reliable type information, I suspect it would be much easier to rely on a single scope for allocating mangled identifiers (with the possible exception of function parameters, which most optimizers already rename locally). So in essence you'd end up with a single "rename dictionary" which would contain all the identifiers that can be safely renamed, such that all instances of someIdentifier get renamed to abc, regardless of their scope. This is sub-optimal, but works fairly well in practice (I believe most products that use Closure do so with the set of flags that causes it to work this way, or at least did as of a few years ago).
An identifier would be considered "rename safe" if the scopes of all references to it can be determined to originate within Typescript source. Any reference via an expression of unknown type (e.g., bar in var foo = untypedFunc(); foo.bar()) or an explicitly external type (e.g., via some_lib.d.ts) would be excluded from renaming. This works to the extent the code contains explicit or inferred type information, and removes the need for an explicit "externs" file, which is very error-prone.
It would be nice to get function/method inlining working as well, but it has stricter requirements. To get much benefit from it, you need to be able to precisely identify all references to the function. Otherwise you can't remove it. This may sound like a small win, but in code with lots of trivial accessor methods, it can really add up.

I'll be posting the minification feature to TsProject this coming week. I am working out the last bits of the implementation for ES5 vs ES6 ( block scoped identifiers using let ).

Here is an example of minifier in TsProject ( whitespace elimination turned off ).
Source:
export class TestClass {
private gerb1: number = 1;
private gerb2: number = 2;
private gerb3: number = 3;
private gerb4: number = 4;

public fPublic(): number {
    let result = this.f1();

    return result;
}

private f1(): number {
    let result = this.f2( 5, 2 ) + this.f3( 19 );

    return result;
}

private f2( parm1: number, parm2: number ): number {
    let result = parm1 + parm2;

    return result;
}
private f3( parm1: number ): number {
    return parm1 + this.f2( 1, 2 );
}

}
var test = new TestClass();
var t1 = test.fPublic();
console.log( t1 );
var TestClass = (function () {
function TestClass() {
this.a = 1;
this.b = 2;
this.c = 3;
this.d = 4;
}
TestClass.prototype.fPublic = function () {
var a = this.e();
return a;
};
TestClass.prototype.e = function () {
var a = this.f(5, 2) + this.g(19);
return a;
};
TestClass.prototype.f = function (a, b) {
var c = a + b;
return c;
};
TestClass.prototype.g = function (a) {
return a + this.f(1, 2);
};
return TestClass;
})();
exports.TestClass = TestClass;
var a = new TestClass();
var b = a.fPublic();
console.log(b);

@ToddThomson Can you compare and post the minified (without whitespace elimination) gzipped file size with the normal gzipped size? Gzip stores a long identifier name that is used multiple only once, so I suspect that mangling private identifiers won't make a big difference.

@ivogabe I'll post statistics when the minifier feature is production ready. I suspect that there won't be great gains over post compilation gulp build tools like uglifyjs. However, my goal is to be able to realize some gains by shortening private identifiers and knowing that the resulting transformed sourcefile will work 100% of the time.

Entropy can be reduced by mangling the names of private variables. 25
instances of separate long names, versus 25 this.a, I expect the
compression of the latter case would be much higher.
Great work Todd!
On Wed, Jan 6, 2016, 5:18 PM Todd Thomson<EMAIL_ADDRESS>wrote:

@ivogabe https://github.com/ivogabe I'll post statistics when the
minifier feature is production ready. I suspect that there won't be great
gains over post compilation gulp build tools like uglifyjs. However, my
goal is to be able to realize some gains by shortening private identifiers
and knowing that the resulting transformed sourcefile will work 100% of the
time.
—
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/8#issuecomment-169492996.

@ToddThomson Glad to hear you're working on this! A couple of quick questions:

If I understand correctly, this minification pass is meant to operate on a single compilation unit. Is that correct?
Is that branch public, or likely to be soon?

@joelgwebber Yes, the main reason I use TsProject is to produce a single bundled output for multiple ES6 modules in separate source files. Yes, it is a public GitHub repository.

Thanks, @ToddThomson. Got a 404 for that branch, but I see now that it's been merged.

@joelgwebber , @bryanerayner I've published TsProject 1.2.0-rc.3 to NPM. It now includes proper handling of class inheritance. I've also added a sample minified typescript app to the GitHub repository.

@RyanCavanaugh I'm not sure how my basic support for identifier minimizing and whitespace elimination can be brought to Typescript. It appears to me that what I've provided in TsProject would be easily achievable within Typescript, but it is something that should be designed and implemented by the core group.

Last post concerning minification in TsProject. With the latest NPM release, I've built TsProject.min.js with full whitespace elimination. I also took the non-minified tsproject.js and ran it through the online JSCompress tool: Here are the results:
TsProject.js 110,908
TsProject.min.js 42,125
TpProject.jscompress: 47,940.
Not startling results, but a pretty good start.

Just to pile on to this issue...  We are currently starting a new JS project and are primarily trying to decide between TypeScript and the Google Closure Compiler.  Both offer the ability to add static typing to JavaScript (ish) applications.  In my view, TypeScript has much better tooling and is in general a more pleasant development experience.  It's integration with IDE's is good, the compiler is much faster than closure, the syntax is better (less verbose), and to tool chain is better.
However, we really do like the ADVANCED_COMPILATION mode.  It can:

More aggressively minify based on type information.
Essentially make private methods unusable through renaming
Makes it harder to reverse engineer

It does significantly better than using something like UglifyJS on transpiled TypeScript.  The files output by the closure compiler are significantly smaller.  Although I a no expert in how this is achieved, as others have mentioned it seems to be that the fact that the "minifier" in closure is type-aware allows it to do more aggressive minification.  UglifyJS (and the like) do not have this benefit.  Doing optimizations and minification inside the type script transpiler should allow it to outperform "bolt on" minifiers.
I am not sure if we will use Closure or TypeScript, but I can say that this particular issue is one of the main decision points for us.

@mmacfadden, I do not see this as an either/or. I know of many teams including Google Angular 2 team that use Typescript + Google Closure Compiler advanced optimizations and are satisfied by the results. You will need something like https://github.com/angular/sickle to convert TS types into JSDoc comments.

@mhegazy, thanks for the reply we had seen a few TS -> closure projects out there but they seemed very out of date.  Although I had searched, I had not come across this project before.  It may be a viable option.  I have two comments:

I do get worried with projects like this only because there are now three things that have to work well together in order for my build to work.  If type script adds a new (relevant) feature, it then needs to be added to sickle.  It also then needs to be something that actually is possible to do in closure.  The Sickle developers are (perhaps) not on the TypeScript team nor the Closure team, so there is some risk there that at some point in the future the two will not play nice together.  Or that the Sickle tool becomes unmaintained.  There is just some risk there.

From a build complexity standpoint I now have two things I need to wrangle instead of one.  Not a big deal, but still a small consideration.

So if I had a magic wand I would love to see this capability right in TypeScript.  I do realize, being a developer myself) that the TS team needs to focus on their sweet spot of functionality and that building a Type Aware minifcation system probably is slightly out of their sweet spot.  I am totally willing to take a look at Sickle and if it works... great, I think my immediate issue will be solved.  But again, assuming a magic wand, I would still love to see this in TS directly.
Again thanks for pointing this project out.  I will definitely take a look.

type aware minification is not all what advanced optimizations in closure compiler. if that is the only thing you need @ToddThomson's work should do the trick.

You are correct.  I am looking for more than just Type Aware minification.  Being type aware does let you do some other interesting optimizations, but you are correct there are more things that it does.  I am also interested in what @ToddThomson is doing.

Hi @mhegazy, would like to know how the angular 2 team use the TypeScript together with Google Closure Compiler. Looks that the uglify is used in the angular2 github repository https://github.com/angular/angular/blob/4a414420e940bf4a5eea2c870ddff6e33f0343b9/gulpfile.js#L1273.

//CC @mprobst from the angular team to answer this question.,

@mmacfadden @raphaelfeng we're still experimenting around, there's no definite answer from our side yet. We're using uglify and seeing good results with that.
We're also building sickle and a compilation toolchain that pipes from TypeScript through Closure Compiler. We hope to possibly give better optimization results from that than uglify itself - mostly due to property renaming, dead code elimination, but also inlining, type specialization, and a host of other tricks. However it is true that it will cause a more complicated build setup expect (e.g. users will need to install Java), and issues when the two tools might not agree. It's hard to know if the end result will be nice to use before trying.
We don't quite know yet where this will lead, but will keep people posted. Keep an eye on github.com/angular/sickle for our progress.

@mprobst, thanks!  I am optimistic that something like sickle could be more successful because the angular team and the typescript teams are working closely together and also because Closure is a Google product.  So it's not as though some independent and unrelated folks are trying to do the work.  Definitely interested in sickle and will monitor the project.

hi
i.ve read this long thread while I need to minimize my code too. I think we need to take the application development productivity point of view.  We can of course use many tools to achieve all the aspects of development, ie prototyping, developping, documenting, productizing, deploying. At the end, we need to be expert in several evolving technologies and we loose time on concerns which are not the main purpise of developpers : focus on the application features. It is why I always promote the integration inside the typescript tooling ecosystem to integrate more features than just grammar and transpiling ones. Tight integration with IDE, debugging', minification, optimisation, uglification, testing coverage, and many more are also very important to increase the development productivity.  I.m a former smalltalk 80 as well as later gwt developper,  and i always aporeciated the all in one box that those technics proposed then. I eager to see in the future the same approach in typescript for a language I more and more fond of nevertheless.
regards
xavier

@mmacfadden Since you mentioned interest, I thought I'd give you a quick update on where I am. I'm working on shortening identifiers which can be considered "internal". I am currently testing the exclusion of identifiers that implement external interfaces.
Shortening of identifiers which can be considered internal is a big gain.

I apologize for making a very long thread longer. Where did this end up? It seems like a lot of people (including me) would love to see a path for minified output, done in a way that takes advantage type information to optimize smaller/better. Lots of us have no preference of whether it happens natively inside TypeScript, via type information passed to uglify, type information passed to Closure, or other means. Do any of these paths already work? Or are all still in various stages of progress?

Kyle: we have Closure minifying code that was processed by sickle
successfully, with property renaming and dead code removal enabled, but
without type based aliasing. We don't have an easy to reproduce build setup
at the moment though :-(
@martine knows more.
Kyle Cordes<EMAIL_ADDRESS>schrieb am Do., 17. März 2016 um
17:26 Uhr:

I apologize for making a very long thread longer. Where did this end up?
It seems like a lot of people (including me) would love to see a path for
minified output, done in a way that takes advantage type information to
optimize smaller/better. Lots of us have no preference of whether it
happens natively inside TypeScript, via type information passed to uglify,
type information passed to Closure, or other means. Do any of these paths
already work? Or are all still in various stages of progress?
—
You are receiving this because you were mentioned.
Reply to this email directly or view it on GitHub
https://github.com/Microsoft/TypeScript/issues/8#issuecomment-198143200

https://github.com/angular/sickle is undergoing active development because we're using it within Google.  Currently Closure's optimizer is hard to use but it's the best optimizer we know of.
Our basic goals/roadmap are:

make every TypeScript program at least accepted by the Closure compiler
support Closure renaming optimizations
support Closure type-based renaming optimizations
make all of this work with no worries

We're somewhere around step 2: current sickle+Closure output successfully applies renaming and minification, which includes inlining, constant propagation, dead code elimination, and all those nice things.
To be clear, you can do a lot of property renaming without any types.  Even if you have two unrelated objects A and B with a internalProperty field, it's safe to rename internalProperty to a everywhere.  Uglify can do that already, I believe.  Types only matter in that when you have a lots of objects -- to be able to rename both A.internalProperty and B.unrelatedInternalProperty to the same a.  Closure supports this and we're working on making the TypeScript types propagate to help.  (Structural typing will likely make this hard...)
Regarding step 4, we'd like to make it so that you can write native-feeling TypeScript code and have all the optimization info trickle into Closure without needing to worry about what Closure will do to your code.  To that end we've done some work on making it so when you use declare var foo; we specially mark the variable foo to prevent renaming.   (There's lots of other work, like restricting TypeScript features that prevent optimization: https://github.com/angular/sickle/issues/23 .)
Our only current client is the internal Google TypeScript build, which internally wraps tsc, so there's not much of a command-line interface to sickle yet.  But it shouldn't be more than a few lines of code for someone to hook it in to their existing build.  If you'd like to look into this I filed https://github.com/angular/sickle/issues/104 to gather requirements.

First of all I think like something like this is really important to generate high quality code and high quality output is something that I would hope for in a stricter tool chain. I understand that the typescript compiler itself isn't necessarily the best place to put it. Maybe a plugin or something would be good because it can still access the rich semantic information of the typescript compiler.
One thing about single-module optimization that many people appear to be overlooking is that renaming private identifiers isn't trivial as assigning a, b, c. This is because subclasses share the same namespace and you don't necessarily know all your subclasses. A simple approach is to use Root.aa, Root.ab, Root.ac in the top level class and Sub.ba, Sub.bb, Sub.bc in the first level class and so on (this would work up to about 60 classes deep). But you still have to worry about subclasses using those names. (maybe Root._tsa_a, Root._tsa_b and Sub._tsb_a, Sub.tsb_b to be pedantic?)
The big question here is also the size of the compilation unit. If we assume whole-program like closure advanced optimizations then you can just rename everything like closure does, however if it is module wide there is very little renaming space that can be used (locals and privates (not protected) are all I can think of right now) because you need to keep "binary compatibility".
Moving past just identifier entropy more "logic" based optimizations can be applied.

Constant prorogation can be applied (this will become possible with other ES6 compressors though).
More inlining because the exact type can be known.

Furthermore if this path is taken annotations that can be added to really allow for smarter compilation. For example pure or const attributes to allow more aggressive (but safe) optimizations.
That being said I see the most gains from intelligent renaming of privates. This is one of the largest things that can't be done by closure and it would be fairly simple to implement. Then you could use a secondary compiler to perform further optimization for the time being.

I also want to see the Closure compiler integration implemented.
Currently we have an internal framework for making HTML5 games and we use Closure Compiler for type checks and minification. Unfortunately we use only SIMPLE optimizations, because when we tried to use the ADVANCED optimizations we found many bugs due to insufficient type annotations on our code.
Typescript compiling to annotated JS code that supports ADVANCED optimizations out of the box would be definitively a killer feature for developers that are still not sure about investing scarce resources to port their code bases to TypeScript.

check out https://github.com/angular/ts-minify

Also http://blog.mgechev.com/2016/07/21/even-smaller-angular2-applications-closure-tree-shaking/ have interesting results.
It seems that the type information allows to reduce the size from 150 to 20 kb for hello world angular 2 application.

+1
If Typescript can't do this then i think it sholud provide plugin ecosystem, so other people can jump into it and  all information that ts have, then no just minifier other things will also be developed like pre-processors etc.

For those who might be interested, I've decoupled the Typescript minifier feature from TsProject so that it is now a standalone node package TsMinifier .
As mentioned above, TsMinifier utilizes the Typescript AST to more effectively mangle identifier symbols. It supports jsDoc annotations ( currently only /** @nomangle */ ) and can optimize mangling through the use of an external namespace convention.

I need help in adding the minifier to my TypeScript compiler options. how to add it to the tsconfig.json ?
how to use the --minify-advanced with source maps ?

@nisimjoseph the TypeScript compiler does not have minification settings. If third party tools are putting additional configuration inside of the compilerOptions property, breakage and incompatibility become likely.

thank you @aluanhaddad ,
I am using Webpack and want to use that Minify Advanced to make the code scrambled.
do you have any guidance for that?

I'm afraid I'm not the person to ask. I have used Webpack's uglify minification support and it works well for me. Closure and tsickle are not roads I want to go down.

I am the author of typescript-closure-compiler.
Basically it is a tool that emits files that are compatible with google closure compile.
I've also created a playground that can show you the generated output.
My TSFunq project uses this tool for minification.

@sagifogel Not to downplay the utility of your tool, but it fails in common idiomatic TypeScript use, resulting in broken JavaScript.
Here is an example

class Greeter {
    greeting: string;
    constructor(message?: string) {
        this.greeting = message || this.defaultGreeting;
    }
    greet() {
        return "Hello, " + this.greeting;
    }
}
interface Greeter {
    defaultGreeting: string;
}
function createGreeter() {
    const greeter = new Greeter('World');
    greeter.defaultGreeting = 'All';
}

produces
"use strict";

/**
 * @constructor
 * @param {string=} message
 */
function Greeter(message) {
    "use strict";
    /**
     * @type {string}
     */
    this.greeting;
    this.greeting = message || this.defaultGreeting;
}

/**
 * @return {string}
 */
Greeter.prototype.greet = function () {
    return "Hello, " + this.greeting;
};

/**
 * @interface
 */
function Greeter() {
}

/**
 * @type {string}
 */
Greeter.prototype.defaultGreeting;

function createGreeter() {
    /** @const {Greeter} */ var greeter = new Greeter('World');
    greeter.defaultGreeting = 'All';
}

For me your definition of "common idiomatic TypeScript" is wrong approach.
I don't think that you should code like that just because you can.
Anyway I'll fix it in the next release.
Thanks.

@sagifogel Sorry, I wasn't trying to be a contrarian.
I agree that my example isn't great code but declaration merging is a very common practice, and it is fairly essential to the language.
More broadly, my point is that trying to reify interfaces is a huge mistake. It causes name collisions immediately which will not only be surprising for TypeScript programmers, because we expect interfaces to never conflict with names in the value space, but also because interfaces are strictly structural in this language. Any system which creates (or relies on) nominal interfaces in JavaScript is a massive change and (IMO) is a terrible direction.

In the limit, you can always write code that will defeat optimization (e.g.
just use eval).  So ultimately you have to choose a subset of JS (and
perhaps even a subset of TS) that you can optimize.
Ideally you could warn the user when they fall out of that subset, though
the "undefined behavior" surprises from C compilers indicate that it's hard
to do perfectly.
Pragmatically we've found that for our users (who likely lean more towards
coming from non-JS languages, and are unlikely to do cutesy JS tricks) a
fairly straightforward translation works out well enough with the Closure
compiler.
Here are some open bugs we have on making our tooling warn when you write
TS that the Closure compiler eats:
https://github.com/angular/tsickle/labels/prevent closure antipatterns
On Fri, Dec 30, 2016 at 9:13 PM, Aluan Haddad<EMAIL_ADDRESS>wrote:

@sagifogel https://github.com/sagifogel Sorry, I wasn't trying to be a
contrarian.
I agree that my example isn't great code but declaration merging is a very
common practice, and it is fairly essential to the language.
More broadly, my point is that trying to reify interfaces is a huge
mistake. It causes name collisions immediately which will not only be
surprising for TypeScript programmers, because we expect interfaces to
never conflict with names in the value space, but also because interfaces
are strictly structural in this language. Any system which creates (or
relies on) nominal interfaces in JavaScript is a massive change and (IMO)
is a terrible direction.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/8#issuecomment-269850019,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAPB6gIA67lP9UcwlayWYxh9jUNHLzMks5rNeR6gaJpZM4CNZxl
.

@evmar My understanding is that Closure's type system is nominal and that @implements is required, I may be wrong (or out of date), but if so that is not even a subset of TS, it is a complete divergence on both the technical and conceptual level and fundamentally incompatible.

I think we're in agreement, then!  My point is that you must be
fundamentally incompatible if you intend to optimize, and it's just a
question of how much incompatibility you're willing to tolerate.
For example you could imagine compiling code that uses TS structural
interfaces in a purely nominal way into an optimized form and fall back on
code that relies on structural typing to generate unoptimized code.  (Also
for this particular point the Closure type system has some structural
typing support, but the general idea stands: worst case you can just do
what uglify does.)
On Fri, Dec 30, 2016 at 10:47 PM, Aluan Haddad<EMAIL_ADDRESS>wrote:

@evmar https://github.com/evmar My understanding is that Closure's type
system is nominal and that @implements is required, I may be wrong (or
out of date), but if so that is not even a subset of TS, it is a complete
divergence on both the technical and conceptual level and fundamentally
incompatible.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/TypeScript/issues/8#issuecomment-269852720,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAAPBxnlu1brJR1vk_sOYFKP8IG1TK4mks5rNfqNgaJpZM4CNZxl
.

Fair enough, but that simply is not TypeScript. It is Closure.
I am not going to start writing Closure.
I know you are not asking me to do that in any way, but bear with me.
I think people should be aware of what they are getting into.
Treating TypeScript as a syntactic veneer over a language other than JavaScript sounds very dubious.
Closure is a subset of JavaScript which, from what I can tell, is based on Java.
Frankly, trying to use a language that didn't even have a way to pass one method to another until 2014 as a basis for statically analyzing JavaScript seems a little off in left field.

Fair enough, but that simply is not TypeScript. It is Closure.
I am not going to start writing Closure.
I know you are not asking me to do that in any way, but bear with me.
I think people should be aware of what they are getting into.
Treating TypeScript as a syntactic veneer over a language other than JavaScript sounds very dubious.
Closure is a subset of JavaScript which, from what I can tell, is based on Java.
Frankly, trying to use a language that didn't even have a way to pass one method to another until 2014 as a basis for statically analyzing JavaScript seems a little off in left field.

Here is a use case
interface Product {
  readonly name: string;
  readonly price: number;
  readonly inStock: boolean;
}

class PowerSaw implements Product { // Error
  constructor(properties: Product, readonly blades: Blade[]) {
    Object.freeze(
      Object.assign(this, properties)
    );
  }
}

function addToOrder(product: Product) { ... }

addToOrder(new PowerSaw()); // Error

By adding
interface PowerSaw extends Product { }

both errors are fixed.

Here is a use case
interface Product {
  readonly name: string;
  readonly price: number;
  readonly inStock: boolean;
}

class PowerSaw implements Product { // Error
  constructor(properties: Product, readonly blades: Blade[]) {
    Object.freeze(
      Object.assign(this, properties)
    );
  }
}

function addToOrder(product: Product) { ... }

addToOrder(new PowerSaw()); // Error

By adding
interface PowerSaw extends Product { }

both errors are fixed.

@aluanhaddad indeed, if you are piping TypeScript through Closure compiler, you are restraining yourself to write in the common language subset of TypeScript and Closure compiler, or more specifically, the language subset your TS->Closure translator enables you to use.
Whether that's practically useful depends on the size of that subset, and a slew of less fundamental and more pragmatic issues (error behaviour, debuggability, optimization quality, etc). An interesting question is also the inverse, that is whether it's pragmatic not to use Closure compiler.
I have first hand experience with several applications where using Closure Compiler as an optimization step after TypeScript is pragmatically useful, and not using would be prohibitive (due to code size). Whether the same is true for you of course depends on your use case and many other factors.
FWIW, the nominal vs structural issue is not actually a problem, Closure compiler supports structural interfaces using @record, and the types vs values namespace issue can of course be fixed by escaping interface names.

I'm partial to having a preset in typescript to emit Closure compatible JS. Basically save types as Jsdoc, check for a couple things that prevent advanced optimizations, and run the right emit settings.
IMO, a tool in the middle shouldn't be necessary. Typescript already has special support for react and different flavors of JS, adding explicit support for Closure would be a great selling point for typescript.
It really just depends on how important it is to the Typescript team. I have a feeling the need for dead code elimination is only going to increase as js gets more widely used, plain minification isn't going to be enough forever. Closure is hands down the best tool for this and the type system is largely compatible already.

@evmar I looked over the tsickle repo and the open issues to see what it currently offers as I may well have to use it at some point in the future.
It seems like there are some rough edges but the commitment that you have shown to abstracting away the differences is really impressive. It seems you have made it a fundamental design priority that the inherent mismatches between the two typesystems be handled internally within tsickle. This is not trivial.  Clearly you value developer experience, and especially as some frameworks don't place enough value on it, I really appreciate it. Thank you ❤

@MGunlogson I think long term the best place for us would be as some sort of plug-in emitter rewrite pass (using the same mechanism TS uses to downlevel async/await, or downlevel ES6->ES5).  But I say plug-in because don't think the TS team wants to own it, as there's a lot of finicky details in making Closure work that unfortunately will require a lot of time.  Depending on how you look at it the type systems are either "mostly compatible" or "fundamentally incompatible".
@aluanhaddad thanks!  I would add that I don't think Closure minification is necessarily the best solution for TS, but rather that it's a pretty good local maximum.  And we're particularly committed to it because we're making TS interop with a large body of existing Closure code.

@RyanCavanaugh

This has been sitting in "Needs Proposal" because, as I've outlined here many times, we're looking for someone to present a compelling case that TypeScript itself can do a substantially better job than off-the-shelf minifiers, in a way that is general-purpose enough to work for most people.

Proposal: Typescript has a tsconfig option "minifyPrivateMembers"
One compelling case is any front end data binding framework such as Angular, Knockout, React, etc. Through data binding we have references to javascript variables in html code. It'd be really nice to be able to mark things as "private" and have those be minified.
Using google closure compiler would require using an externs file for all the public properties I want to keep. That would be awful to hand maintain. Perhaps something could generate an externs file out of everything that's not marked private and then run closure compiler, but it'd be much nicer if typescript could just minify anything marked private. Typescript wouldn't even have to minify anything else, uglify can do the rest.
I have a library using typescript with knockout, and it's 418,828 bytes with uglify and 302,158 bytes with closure compiler. That's about 28% smaller. I'd very roughly estimate real savings would only be half that, since that closure compiler result also obfuscated public things.

@mblandfo , can you submit a more formal proposal as a separate issue? I think that has a better chance of being noticed by the core team, and would provide a better place to discuss the specifics of what you're after.
I think just having that one setting would be enough - especially since we now have a plugin compatible compiler, the time has come for this to see the light of day.

I think tsc should definitely support this, since it can do related optimization better than 3rd party tools. For example, using enums result in a bunch of useless code, adding string mappings and whatnot.
However, a minifier option would imply no undeclared external references.  tsc would then be able to make optimizations like replacing all enum usage with numeric literals.

@mrkite Did you know that const enums exists?

+1 for this issue.

https://yoric.github.io/post/binary-ast-newsletter-1/

Maybe it'll be useful for someone - I've written a transformer, which (I believe it'll rename privates at all to random name by itself in the future) renames private class' members in a form of _prefix_originalName (where _prefix_ can be changed and by default is _private_; originalName - original name of the private member), so you can use terser/uglify with a mangle option to minify private properties/methods.
After smoke testing it allows me reduce application's size up to ~15% min and ~5% min.gz (from 43kB to 41kB min.gz or from 186kB to 157kB min in my case).

https://github.com/timocov/ts-transformer-minify-privates
https://www.npmjs.com/package/ts-transformer-minify-privates

This experimental project is using Typescript's type information for the minification https://github.com/angular/ts-minify

I would be curious to hear if anybody successfully uses https://github.com/angular/ts-minify ...  Looks like the last (real) commit was 4 years ago. 🤔

I believe ts-minify was an intern project experiment that never(?) was used in production.  The intern host (rkirov, some commits on that project) has since worked on tsickle, which we still support (discussed upthread on this bug).

What @evmar says is correct. It was an experiment and never used in production. Looking back at it now, I am convinced that using TS types for making optimization decisions is not worth the trouble. There are two main reasons:

it is too painful to debug misoptimized applications and in big programs TS types are not correct with the fidelity needed for these painful scenarios to not occur too often.
Structural types allow for very easy flow of concrete objects through abstract types (think interfaces). An optimizer wants to connect the concrete object creation with the concrete usage site, but the TS type-checker will not directly give it that information. An optimizer will have reconstruct a lot of information through a global walk through the whole program. At that point the optimizer starts to look like a Hindley-Milner type-inferencer for a stricter type system. It is not clear to me what have we gained by using the TS types.

Here is an example for 2)
interface I {a: string, b: string};
interface I2 {a: string};

const x: I = {a: '', b: ''};  // construction site

function f(x: I2) {
  x.a;  // usage site
}

f(x);

The optimizer needs to know whether a: '' is ever used, so it needs to find x.a in f but that has to go through all f call-sites. The abstract types I and I2 don't make that easier, as one has to find all assignability points, i.e. knowing that f(x) exists and that I does get assigned into I2. Add generics into this mix and hopefully you are terrified.
I have a longer write up that is not ready to be shared, but my personal conclusion is:

Types tend to become a synonym for any static analysis. However, type systems are designed with
certain use cases in mind. The TS type system is designed for ease-of-use and unobtrusiveness to
JS developers. Making use of the TS types for optimizations is a major stretch of the original intent,
and it is not clear whether optimizers would be better served by starting from scratch (code without
types).

Caveat here being that this is all separate from using the TS toolchain - AST, transformers for optimizations. Those can be great to reuse.

I hope ppl in Microsoft are better than ppl from Google at cutting corners and killin two rabbits in one shot: provide minification capability and keep it bug/undefined behaviour -free <3 high hopes energy here

Dont worry about waiting for this support to be available cuz it will be a
long time wait.
Instead go with npm package or angular whatever webpack/aot to do thr
javascript minification for you.  Thats what we did w/ the npm package &
added this short command script to get this job done instead of waiting for
it
On Wed, Nov 20, 2019, 10:38 PM Victor Mus<EMAIL_ADDRESS>wrote:

I hope ppl in Microsoft are better than ppl from Google at cutting corners
and killin two rabbits in one shot: provide minification capability and
keep it bug/undefined behaviour -free <3 high hopes energy here
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/microsoft/TypeScript/issues/8?email_source=notifications&email_token=ABHAL6PKAJ726D3PE2GNQCDQUX7B5A5CNFSM4ARVTRS2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOEEY3IZA#issuecomment-556905572,
or unsubscribe
https://github.com/notifications/unsubscribe-auth/ABHAL6PUYT35HX2VBK5URJTQUX7B5ANCNFSM4ARVTRSQ
.

Using TypeScript type information to get better minification is a large discussion with many potential avenues and pitfalls. @rkirov's I / I2 example is a great example and indeed if I try to use VS Code to rename a, it fails to rename all the instances and you end up with compile errors! If VS Code doesn't get it right on a single symbol, it's unlikely a minifier would get it right on a whole codebase!
But that doesn't mean type information isn't useful. There are things that UglifyJS doesn't do or doesn't do well, that I think can be improved with type information.  In particular, I'm interested in property renaming. UglifyJS doesn't enable this by default and the docs say "Note: THIS WILL PROBABLY BREAK YOUR CODE".
But property renaming isn't hard for the reason @rkirov highlighted.  The way it works in Closure and UglifyJS, etc. is properties are renamed globally.  This is a "clever" trick that means in the I / I2 example, it'll just automatically rename all instances of a to the same thing even though the minifier has no idea if I and I2 are related or not.  It just sees a and replaces it with xyz123 or whatever.
This is a reasonably safe way to rename properties since you're guaranteed that all usages of property a will be renamed to the same thing regardless of where they appear.  It's probably slightly non-optimal (since you need to rename onto globally unique names even if they're never used in the same context) but that's not that big of a concern.
The (main) challenge with existing property renaming (and the reason UglifyJS says it will break your code and Closure has a long list of dangers / workarounds when enabling advanced optimizations) is that it's only safe if your code exists in a vacuum with no interaction with other APIs (e.g. the DOM, other SDKs, etc.) or services (e.g. REST calls to your backend).  It's safe to rename MyApp.document but not window.document and it's safe to rename MyMessage.text but not text in let databaseRequest = { text: '...' }.
NOTE: This isn't the only challenge.  JS has lots of dynamic ways of accessing properties, etc.  If you do x['te' + 'xt'] the minifier probably won't know that you are accessing x.text which might have been renamed.  But you can avoid most of these via reasonable coding conventions (and a TypeScript minifier could notice / warn you when you're doing problematic things like this).
So you you need some sort of heuristic approach to figure out what's safe to rename (i.e. it's 100% internal to your app/library) and what's not (i.e. it crosses the boundary into the outside world).  A simple solution is to use naming conventions (e.g. only rename properties that you've prefixed with _). But that pollutes your codebase and requires ongoing manual auditing.  It would be better if perhaps you were only forced to use naming conventions for exceptional cases where the builtin heuristics don't work automatically.
And here's the thing.  TypeScript types give you a lot of extra information to build your heuristics on. window is of type Window which comes from lib.dom.d.ts which is external to your app. So you can't rename any properties on it.  If databaseRequest is passed to SuperDB.write() which is defined in a super-db.d.ts file from an external npm package, it's probably not safe to rename properties on it either.
Is it foolproof? No.  Is it better than UglifyJS can do without types?  Absolutely.  It'll probably end up being roughly equivalent to what Closure does (where you use externs to declare what's external to your app, etc.) but given the rich type information that exists for TypeScript (minimal use of any, comprehensive d.ts files, etc.), it should require less manual tweaking.  And even if it ends up equivalent to Closure, Closure is a beast to deal with and not very user-friendly.
So I think there's a big opportunity to get Closure-level minification but with less effort (due to better type information) and a better developer UX.

Would it be a valid argument to say tsconfig should include a minify option simply to allow decoupling from another tool (for example, gulp)? We currently have a project using typescript that is using gulp and npm only for the purpose of javascript minification. If tsconfig had a minify option, then we'd be able to completely break our project free from gulp and the npm ecosystem.

I also want to use Typescript for minification to free me from Nodejs.

2020, The TypeScript is static language - cannot remove unused code..

I was starting to love TypeScript, but then I discovered that the compiler does not support any optimization nor dead code removal. There are many things that it could optimize thanks to its knowledge about... Well, types! Think of constant propagation.
Oh, and finally there's a reason that IMHO makes the effort worth by itself: not involving the usage of a complex, multi-step build pipeline that could break in many different points. If there is a bug caused by your code generation process you have to look at typescript, not at bundler+loader+typescript+various_plugins.

@falcon03 I mostly agree with you, but I'm not sure if optimization can be thrown in the same bin as minification (even optimization might reduce code size).
For constant folding/propagation, you might want to look at #3964 which discusses the topic. Unreachable/dead code removal on the other hand could be detected by the IDE and shown to the user (maybe also by the compiler as warning), but not emitting does not seem to be a safe approach to me, for instance if one adds some safety net for when the type passed into a call is not what the compiler knew at compile time.

Hi there,
if you're looking for property renames optimizations (for instance like google closure compiler does in advanced mode) you can try https://github.com/timocov/ts-transformer-properties-rename - it's the next generation of ts-transformer-minify-privates from https://github.com/microsoft/TypeScript/issues/8#issuecomment-500379375, but this one might detect all internal properties and rename them as well.

@falcon03 I’ve been using bundlers, transpilers, typescript, uglifiers, loaders, webpack plugins for 5+ years. The number of times I had to do any debugging for the purpose of figuring out why the output is wrong due to a bug is... 1 (literally one). And that was when I was experimenting with experimental ES6 minifiers 3 years ago and Safari 10 had a bug with a certain ES6 syntax, it wasn’t a bug with my pipeline apparently.
Just saying.

AssemblyScript already emits the most minified, non-human-readable code that a TypeScript compiler should ever need to, and from what I hear, It's well-optimized too!
No, it's not TypeScript, it's a subset, but that might change as time goes on.
By minifying TSC output, the TS developers essentially encourage the *(mis)*usage of JavaScript as a compilation target.
At minimum, one should target asm.js, but it has been deprecated in favor of Wasm.
This is probably worth closing and saying that TSC removes bugs and headaches, not whitespace; that is already a very important feature that we are all grateful for ❤.
The TypeScript development team should focus on TypeScript, minification, optimization, and related issues should be taken on by the community, by using the syntax and type output API that TSC already offers.

AssemblyScript already emits the most minified, non-human-readable code that a TypeScript compiler should ever need to, and from what I hear, It's well-optimized too!

I really like the way you look into this issue. The JS target of AssemblyScript uses types to produce the code. Deeding it the produced code to terser further reduces the size.
I actually raised this once in one of the discussions with the core team of AssemblyScript. For me, the reason that the JS target is useful is that DOM API is still missing in WASM, and so WASM does not completely replace JavaScript in this regard.
There are still some things to improve. For example, I had some concerns with reusing the modules produced this way (e.g. if the modules are ES6 compliment, etc), or if all the generated globals are needed for js target.
cc: @maxgraey @torch2424

@aminya

...The JS target of AssemblyScript uses types to produce the runtime optimized code.
is compiled to the following which hints V8 to optimize this code for integer types instead of floating-point numbers.
let x = foo() | 0

Not only does that hint ES implementations/runtimes to use 32-bit integers, but it also forces them to. For example, if foo were an imported JS function, that would coerce the result into an int.
This also ensures that the runtime semantics of AS are preserved. Let's say that the JS function foo was defined as:
const foo = () => 0xffff * 0x10000;

This would result in an overflow, but now the AS generated JS would have the same behavior as the Wasm.
There are more interesting cases, such as i64, how do they handle that? 64-bit integers would have to represented as JS BigInts in order to maintain precision.

I actually raised this once in one of the discussions with the core team of AssemblyScript. For me, the reason that the JS target is useful is that DOM API is still missing in WASM, and so WASM does not completely replace JavaScript yet.

Of course not, Wasm doesn't replace JS, at least not yet.
It was originally intended that authors would write parts of their code in normal JS, and performance-critical parts in asm.js.
Now Wasm replaces asm.js, but they still fill the same role.
But, with the newer interfacing types proposals and such, it's not infeasible to imagine a Wasm controlled front-end web.
When Wasm has native access to WebIDL types, you can expect quite a change.
But in the meanwhile, someone at AS should try to mimic Rust Wasm Bindgen's ABI. It gives some access over JS, while the control flow primarily still remains in Wasm. Differentiating between JS objects and Wasm objects may get complicated, they may need to introduce a type-level API to allow this type of functionality.

@RyanCavanaugh  👋 Hi there I am working on a command-line wrapper for typescript which will minify the code in the output file with the uglifyjs library I will release it soon.

TypeScript should support emitting minified JavaScript.
There are several different things we could support:

Just remove whitespace
Minify unobservable identifiers
Remove provably dead code
Whole-program minification (i.e. closure compiler)
(Others?)

Okay I got it

@aadityasivaS can you please consider terser instead of uglifyjs?

@aadityasivaS can you please consider terser instead of uglifyjs?

okay

@RyanCavanaugh 👋 Hi there I am working on a command-line wrapper for typescript which will minify the code in the output file with the uglifyjs library I will release it soon.

To be clear, the output file is javascript and can already be processed by many different tools. This issue is about minifying during the Typescript compilation itself using information that only Typescript has.
Do you have some way of capturing, transferring, and using the Typescript metadata with the other minifier tooling? Otherwise just running uglify/terser on the output is not the solution.

@RyanCavanaugh 👋 Hi there I am working on a command-line wrapper for typescript which will minify the code in the output file with the uglifyjs library I will release it soon.

To be clear, the output file is javascript and can already be processed by many different tools. This issue is about minifying during the Typescript compilation itself using information that only Typescript has.
Do you have some way of capturing, transferring, and using the Typescript metadata with the other minifier tooling? Otherwise just running uglify/terser on the output is not the solution.

oh okay
anyway thanks

Whole-program minification (i.e. closure compiler)

Well... if you really want a reason to give up, here I go.
Let's use everyone's favorite function: a Fibonacci function!
Take this JavaScript Fibonacci implementation:
const sqrt5 = 5.0 ** 0.5;
const phi = (sqrt5 + 1.0) / 2.0;

export const fib = n => {
	const phi_pow = phi ** n;

	const fraction = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

Maximally optimizing this would be illegal.
This is a whole lot of instructions: multiple exponentiation operations, assignment ops, division ops, room for two ToPrimitive ops, subtraction, property lookups, function calls, addition ops, variable lookups, scope lookups, etc.
Take this input:
fib({
    [Symbol.toPrimitive]: () => {
        // ...
    }
});

This will cause these two lines to call the dreaded toPrimitive ops:
const phi_pow = phi ** n;

const fraction = -phi ** -n;

And that, is how you cause deoptimization to happen in any JS runtime :)
Honestly, I don't even want to put in the mental effort to optimize that.
Let's take the TS version:
type f64 = number;

const sqrt5: f64 = 5.0 ** 0.5;
const phi: f64 = (sqrt5 + 1.0) / 2.0;

export const fib = (n: f64): f64 => {
	const phi_pow: f64 = phi ** n;

	const fraction: f64 = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

To start, we can perform constant folding on sqrt5 and phi:
const sqrt5: f64 = 2.23606797749979;
const phi: f64 = 1.618033988749895;

export const fib = (n: f64): f64 => {
	const phi_pow: f64 = phi ** n;

	const fraction: f64 = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

Cool, let's inline those:
export const fib = (n: f64): f64 => {
	const phi_pow: f64 = 1.618033988749895 ** n;

	const fraction: f64 = -1.618033988749895 ** -n;

	return Math.floor((phi_pow + fraction) / 2.23606797749979);
};

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:
const fraction: f64 = 1.0 / -1.618033988749895 ** n;

then move around the negation
const fraction: f64 = -1.0 / 1.618033988749895 ** n;

and then we can de-duplicate the entire exponentiation operation and variable lookup!
const fraction: f64 = -1.0 / phi_pow;

You know what? I'll skip the rest, no one cares about the process:
Here's the best that an advanced optimizing compiler could do that would be legal with TypeScript:
export const fib = (n: f64): f64 => Math.floor(
	1.0 / (n = 1.618033988749895 ** n) + n / 2.23606797749979
);

Go find me the minifier that can out perform that, I dare you.
Closure compiler can't even do that with advanced optimizations.
Yet, it's not the best that it could be, performance-wise.
We have an unnecessary lookup on the Math object.
Hoisting it, we would get this:
type f64 = number;

const { floor } = Math;

export const fib = (n: f64): f64 => floor(
	1.0 / (n = 1.618033988749895 ** n) + n / 2.23606797749979
);

Does anyone want to benchmark that func and compare it to AssemblyScript's hello world fib func to prove that plain TypeScript out-performs AssemblyScript?
That is my hand-optimized code for you, do you know how hard it would be to do this for every single line of code across my codebase?
Stripping whitespace, the size of the text is 93 bytes. Equivalent Wasm would be 132 bytes, and most of it is just the Wasm header bytes. ~39 bytes, imho, it's not even worth it.
Of course, this is a trivial example, compiling entire web applications show much more major differences, but in the end, this is mistargeting JS as a compilation target, not to even talk about TS's any type and such, that create loopholes in the type system, effectively making it as good as JS anyway.

Whole-program minification (i.e. closure compiler)

Well... if you really want a reason to give up, here I go.
Let's use everyone's favorite function: a Fibonacci function!
Take this JavaScript Fibonacci implementation:
const sqrt5 = 5.0 ** 0.5;
const phi = (sqrt5 + 1.0) / 2.0;

export const fib = n => {
	const phi_pow = phi ** n;

	const fraction = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

Maximally optimizing this would be illegal.
This is a whole lot of instructions: multiple exponentiation operations, assignment ops, division ops, room for two ToPrimitive ops, subtraction, property lookups, function calls, addition ops, variable lookups, scope lookups, etc.
Take this input:
fib({
    [Symbol.toPrimitive]: () => {
        // ...
    }
});

This will cause these two lines to call the dreaded toPrimitive ops:
const phi_pow = phi ** n;

const fraction = -phi ** -n;

And that, is how you cause deoptimization to happen in any JS runtime :)
Honestly, I don't even want to put in the mental effort to optimize that.
Let's take the TS version:
type f64 = number;

const sqrt5: f64 = 5.0 ** 0.5;
const phi: f64 = (sqrt5 + 1.0) / 2.0;

export const fib = (n: f64): f64 => {
	const phi_pow: f64 = phi ** n;

	const fraction: f64 = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

To start, we can perform constant folding on sqrt5 and phi:
const sqrt5: f64 = 2.23606797749979;
const phi: f64 = 1.618033988749895;

export const fib = (n: f64): f64 => {
	const phi_pow: f64 = phi ** n;

	const fraction: f64 = -phi ** -n;

	return Math.floor((phi_pow + fraction) / sqrt5);
};

Cool, let's inline those:
export const fib = (n: f64): f64 => {
	const phi_pow: f64 = 1.618033988749895 ** n;

	const fraction: f64 = -1.618033988749895 ** -n;

	return Math.floor((phi_pow + fraction) / 2.23606797749979);
};

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:
const fraction: f64 = 1.0 / -1.618033988749895 ** n;

then move around the negation
const fraction: f64 = -1.0 / 1.618033988749895 ** n;

and then we can de-duplicate the entire exponentiation operation and variable lookup!
const fraction: f64 = -1.0 / phi_pow;

You know what? I'll skip the rest, no one cares about the process:
Here's the best that an advanced optimizing compiler could do that would be legal with TypeScript:
export const fib = (n: f64): f64 => Math.floor(
	1.0 / (n = 1.618033988749895 ** n) + n / 2.23606797749979
);

Go find me the minifier that can out perform that, I dare you.
Closure compiler can't even do that with advanced optimizations.
Yet, it's not the best that it could be, performance-wise.
We have an unnecessary lookup on the Math object.
Hoisting it, we would get this:
type f64 = number;

const { floor } = Math;

export const fib = (n: f64): f64 => floor(
	1.0 / (n = 1.618033988749895 ** n) + n / 2.23606797749979
);

Does anyone want to benchmark that func and compare it to AssemblyScript's hello world fib func to prove that plain TypeScript out-performs AssemblyScript?
That is my hand-optimized code for you, do you know how hard it would be to do this for every single line of code across my codebase?
Stripping whitespace, the size of the text is 93 bytes. Equivalent Wasm would be 132 bytes, and most of it is just the Wasm header bytes. ~39 bytes, imho, it's not even worth it.
Of course, this is a trivial example, compiling entire web applications show much more major differences, but in the end, this is mistargeting JS as a compilation target, not to even talk about TS's any type and such, that create loopholes in the type system, effectively making it as good as JS anyway.

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 it may possible. That's why Closure Compiler, Pprepack or LLVM / GCC newer rewrite this as you proposed.

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 it may possible. That's why Closure Compiler, Pprepack or LLVM / GCC newer rewrite this as you proposed.

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 ... it may be possible but I'm not sure even in this case. That's why Closure Compiler, Pprepack or LLVM / GCC newer rewrite this as you proposed.

Just to confirm, that "newer" was meant to be "never," right?
These are very valid concerns, no one wants to debug their code's mysterious precision loss, but it can help in very, very tightly executing code and reduce code size nonetheless, therefore we would have to put this under the suggested:

(Others?)

it appears that we would need a --fast-math flag or something of the sort, further complicating TSC.

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 ... it may be possible but I'm not sure even in this case. That's why Closure Compiler, Pprepack or LLVM / GCC newer rewrite this as you proposed.

Just to confirm, that "newer" was meant to be "never," right?
These are very valid concerns, no one wants to debug their code's mysterious precision loss, but it can help in very, very tightly executing code and reduce code size nonetheless, therefore we would have to put this under the suggested:

(Others?)

it appears that we would need a --fast-math flag or something of the sort, further complicating TSC.

Just to confirm, that "newer" was meant to be "never," right? Yeah, just typo

Just to confirm, that "newer" was meant to be "never," right? Yeah, just typo

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 ... it may be possible but I'm not sure even in this case. That's why Closure Compiler, Prepack or LLVM / GCC never rewrite this as you proposed.

Don't they do this with /Ofast? /Ofast allows applying the optimizations that may cause precision loss.

Because the parameter n should always be a number, we can forget about those deopts that I had mentioned earlier, let's do something tricky, like... rewriting those exponentiation operations:

Btw rewriting C ** -y -> 1 / (C ** y) is illegal in general and will cause to precision lost. Only if C is power of two like 2.0, 4.0, 16.0 ... it may be possible but I'm not sure even in this case. That's why Closure Compiler, Prepack or LLVM / GCC never rewrite this as you proposed.

Don't they do this with /Ofast? /Ofast allows applying the optimizations that may cause precision loss.

Yes, but it's not relate to -Oz (which is optimization for size). As I undersatand main goal of this proposal is minification but not optimize especially with loose some precision and potentially broke logic. So C ** -y -> 1 / (C ** y) in general case is not minify code, and may be shrink only if we have similar expression like C ** y which mean optimize should not only provide peephole optimizations but also provide more high level analysis like cost analysis, sophisticated matching expressions which more smart than CSE (common sub-expression elimination). In this case TS will become compiler similar to LLVM, Ocaml or Go. I'm not sure this goal of TypeScript)

Yes, but it's not relate to -Oz (which is optimization for size). As I undersatand main goal of this proposal is minification but not optimize especially with loose some precision and potentially broke logic. So C ** -y -> 1 / (C ** y) in general case is not minify code, and may be shrink only if we have similar expression like C ** y which mean optimize should not only provide peephole optimizations but also provide more high level analysis like cost analysis, sophisticated matching expressions which more smart than CSE (common sub-expression elimination). In this case TS will become compiler similar to LLVM, Ocaml or Go. I'm not sure this goal of TypeScript)

Well, as discussed by other people, regarding the size optimizations, I am not convinced that TypeScript can do anything much different than what Terser and Closure Compiler are doing. Especially, when you can override things with any, as, and different projects may use different levels of strictness. You may able to use the types to apply some basic size reduction, but that is not going to be groundbreaking.
That's why I think a mixed target or a js-only target for AssemblyScript is a much better approach for applying "real" optimizations.

Well, as discussed by other people, regarding the size optimizations, I am not convinced that TypeScript can do anything much different than what Terser and Closure Compiler are doing. Especially, when you can override things with any, as, and different projects may use different levels of strictness. You may able to use the types to apply some basic size reduction, but that is not going to be groundbreaking.
That's why I think a mixed target or a js-only target for AssemblyScript is a much better approach for applying "real" optimizations.

It would be nice if names of private properties and methods of a class would be minified/mangled by the typescript compiler.
That is something what Terser or UnglifyJS cannot do, because after transpiling from TS to JS this information gets lost.

It would be nice if names of private properties and methods of a class would be minified/mangled by the typescript compiler.
That is something what Terser or UnglifyJS cannot do, because after transpiling from TS to JS this information gets lost.

Don't they do this with -Ofast? -Ofast allows applying the optimizations that may cause precision loss.

-Ofast
Disregard strict standards compliance. -Ofast enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on -ffast-math, -fallow-store-data-races and the Fortran-specific -fstack-arrays, unless -fmax-stack-var-size is specified, and -fno-protect-paren

Ha, I doubt my Rustc has that type of flag.
Sounds like it might, I'd have to check.
Mathematically, that is, assuming infinite precision, the expressions are equivalent.

Yes, but it's not relate to -Oz (which is optimization for size).

In that particular case, it did shorten the size, but in general, it wouldn't even help.

... and may be shrink only if we have similar expression like C ** y in same scope...

That is precisely why I had done it there, if the expression were not there, I wouldn't have done it at all.

Don't they do this with -Ofast? -Ofast allows applying the optimizations that may cause precision loss.

-Ofast
Disregard strict standards compliance. -Ofast enables all -O3 optimizations. It also enables optimizations that are not valid for all standard-compliant programs. It turns on -ffast-math, -fallow-store-data-races and the Fortran-specific -fstack-arrays, unless -fmax-stack-var-size is specified, and -fno-protect-paren

Ha, I doubt my Rustc has that type of flag.
Sounds like it might, I'd have to check.
Mathematically, that is, assuming infinite precision, the expressions are equivalent.

Yes, but it's not relate to -Oz (which is optimization for size).

In that particular case, it did shorten the size, but in general, it wouldn't even help.

... and may be shrink only if we have similar expression like C ** y in same scope...

That is precisely why I had done it there, if the expression were not there, I wouldn't have done it at all.

It would be nice if names of private properties and methods of a class would be minified/mangled by the typescript compiler.
That is something what Terser or UglifyJS cannot do because after transpiling from TS to JS the "public/protected/private" information is lost.

It can't be done anyway, the only reason to use private foo over #foo is to use it outside of the class, e.g. testing.
Can't Terser/UglifyJS minify #foo in a class?

It would be nice if names of private properties and methods of a class would be minified/mangled by the typescript compiler.
That is something what Terser or UglifyJS cannot do because after transpiling from TS to JS the "public/protected/private" information is lost.

It can't be done anyway, the only reason to use private foo over #foo is to use it outside of the class, e.g. testing.
Can't Terser/UglifyJS minify #foo in a class?

Ha, I doubt my Rustc has that type of flag.
Sounds like it might, I'd have to check.

No, it's can't. See this playground.
https://godbolt.org/z/GvY4Pd
As you can see in both cases (without and and with -ffast-math) LLVM generated two calls

Ha, I doubt my Rustc has that type of flag.
Sounds like it might, I'd have to check.

No, it's can't. See this playground.
https://godbolt.org/z/GvY4Pd
As you can see in both cases (without and and with -ffast-math) LLVM generated two calls

C++ uses exp2 instead of pow when Ofast is used instead of O3.
https://godbolt.org/z/j676T5

C++ uses exp2 instead of pow when Ofast is used instead of O3.
https://godbolt.org/z/j676T5

C++ uses exp2 instead of pow when Ofast is used instead of O3.
Yes, it's just minor performance optimization. JS Math hasn't Math.exp2 for example, but anyway it will be more code size than x ** y

C++ uses exp2 instead of pow when Ofast is used instead of O3.
Yes, it's just minor performance optimization. JS Math hasn't Math.exp2 for example, but anyway it will be more code size than x ** y

Yes, it's just minor performance optimization. JS Math hasn't Math.exp2(x, y) for example, but anyway it will be more code size than x ** y

In Wasm, these would both be equivalent in size:
local.get $x
local.get $y
call $Math::exp2

local.get $x
local.get $y
call $Math::pow

Which is really just debug info and can be
local.get 0
local.get 1
call 0

Allowing one to optimize Wasm for performance, without size costs, whereas using Math.exp2 in JS would cost two extra bytes per call, with a header size cost:
// const f = Math.exp2;
f(x,y);
x**y;

Yes, it's just minor performance optimization. JS Math hasn't Math.exp2(x, y) for example, but anyway it will be more code size than x ** y

In Wasm, these would both be equivalent in size:
local.get $x
local.get $y
call $Math::exp2

local.get $x
local.get $y
call $Math::pow

Which is really just debug info and can be
local.get 0
local.get 1
call 0

Allowing one to optimize Wasm for performance, without size costs, whereas using Math.exp2 in JS would cost two extra bytes per call, with a header size cost:
// const f = Math.exp2;
f(x,y);
x**y;

In Wasm, these would both be equivalent in size:

No really. Wasm hasn't builtin intrinsics for pow and exp2. That's why it call stdlib instead use something like f64.pow. It means code which use two call $Math::pow will be always smaller than code which call one call $Math::pow and one call $Math::exp instead

In Wasm, these would both be equivalent in size:

No really. Wasm hasn't builtin intrinsics for pow and exp2. That's why it call stdlib instead use something like f64.pow. It means code which use two call $Math::pow will be always smaller than code which call one call $Math::pow and one call $Math::exp instead

This discussion seems to be veering pretty far off-topic; can we reel it in a bit please?

This discussion seems to be veering pretty far off-topic; can we reel it in a bit please?

There's a significant slice of JS/TS developers that don't care that much about bundle sizes, which contributes to the feeling some people have that JS/TS apps are bloated almost by definition.
It'd be amazing to see a critical player in the ecosystem like TypeScript taking a stance on this, offering an amazing minifier that leveraging the type system is able to deliver state-of-the-art minification ratios.

There's a significant slice of JS/TS developers that don't care that much about bundle sizes, which contributes to the feeling some people have that JS/TS apps are bloated almost by definition.
It'd be amazing to see a critical player in the ecosystem like TypeScript taking a stance on this, offering an amazing minifier that leveraging the type system is able to deliver state-of-the-art minification ratios.

TypeScript should support emitting minified JavaScript.
There are several different things we could support:

Just remove whitespace
Minify unobservable identifiers
Remove provably dead code
Whole-program minification (i.e. closure compiler)
(Others?)

I would be happy with the easiest thing you could do (number 1)
We have a script that transpile a separate repo right into our code base using an older version of ts,
Theres is a MD file that says 'generated code do not edit' but it's easy to ignore that and forget.
Having the simplest minification option would work far better at communicating to developers that this code is generated and not mean for editing.
Yes this can be done with additional tools and more lines in a script but a flag like '"whitespace": false' would be far nicer.
Our regular production build process does 2-5

I would be happy with the easiest thing you could do (number 1)

I agree. Just removing whitespaces would decrease my .js sizes by 10%~20%. This could really be an option at the tsconfig that isn't hard at all to implement (they already exist!). Later more complex optimizations could be added, using other options at the tsconfig.
I don't think devs should necessarily use another tool for removing whitespaces. This is simple enough and a very very very common practice.

I feel this discussion departs from the real reason this is important. Per Ryan's very early comment:

Motivating examples of things that TypeScript could minify but an external minifier could not would be useful. Things like closure and uglify do a really good job already; we'd have to have evidence we could make real improvements over them to justify spending time on it.

I think that really doesn't do justice for why this ticket is so important. It's important because it would save developer's time.
I saw this thread years ago. Why? Because one of my first use cases was "Damn, this product is sweet. Can't wait to minify with it. Let's google how to do that."
And I'm here now. Why? Because my first thought was "Sweet, I love this. Can't wait to minify it. Let's google how."
And this is ticket #8. Why? Because every other developer that uses this project thinks the same thing.
Yes, we COULD go download some additional third party software. But a lot of devs, like me, don't want to add yet another third party tool, don't want to learn another tool. We don't need it to be 10% faster than the alternative.
We just want it to be there because it's simple, it saves us time, and it makes our lives a lot easier. I'm finding myself baffled that this ticket still exists when it's such an otherwise amazing project with such a mind-boggling choice to not include this when MS is typically so good at designing for simplicity and user experience.

Yes, we COULD go download some additional third party software. But a lot of devs, like me, don't want to add yet another third party tool, don't want to learn another tool. We don't need it to be 10% faster than the alternative.
We just want it to be there because it's simple, it saves us time, and it makes our lives a lot easier.

Your comment is perfect.
I don't like bloating my programs with libraries and extra steps. I like simplicity and development speed. Life is too short to learn and use tons of stuff to get something working that could be way simpler.
The main point here is that it could really be done easily, and by that i mean just removing the whitespaces, that would already be a very good functionality.
As I said before, it isn't needed to implement all the possible minifiers at once. Having in the tsconfig a "minify": true for the recommended available minifications and/or "minify": {"whitespace": true} would be perfect.

so its been almost 10 years, and typescript devs refuse to add a simple minifier?

so its been almost 10 years, and typescript devs refuse to add a simple minifier?

I assume nobody has any problem with that you @uzbeki just create a draft PR and we can start see how you implement this simple minifier. Please also maintain it the whole way in all future versions of TypeScript as this would then we a core part of TypeScript.
Alternatively just use external tools like esbuild and go on 🤷

It's an unnecessary overhead to use Webpack (although config can be made by copy-paste), but @Randagio13  idea with postbuild is quite simple and can be implemented in seconds by anyone, when needed without changes in TypeScript

Your comment is perfect.
I don't like bloating my programs with libraries and extra steps. I like simplicity and development speed. Life is too short to learn and use tons of stuff to get something working that could be way simpler.

Not to mention, it'd make it much easier to get a basic level of minification AND working source maps when the project and the person/team knowledge are primarily some non-JS/TS server-side language like Rust.

With all available tools and implementations out there, and given that this issue... is over 8 years old!! It's nice to have new TypeScript language features, but can we please have some feature complete implementation for the current more tricial issues?

After pondering this one for cough a while, we've decided that minification is pretty clearly out of scope for the TypeScript project and will remain so for the foreseeable future.
Broadly, this boils down to two key discussion points:

Could TypeScript minify better than other minifiers in the space? If so, at what cost?
Is putting a good-enough minifier "in the box" worth it?

Regarding the first point, after looking at what modern minifiers can do, what the type system requires of us (which is to say, no type-directed emit), and what other systems which do semantic-informed minification (Closure) do, the cost-benefit ratio is simply not there. Type-directed minification is not desirable or feasible. With types off the table, there's really nothing that TypeScript can do (prior to stripping the types, or immediately after) that other tools can't do an equally good job of. Given our finite time and complexity budget, the best move in terms of increasing the overall goodness of the JavaScript ecosystem is to invest our resources in places where we can provide a value-add in this space.
Regarding "good enough" minification, there's not a lot of meat left on that bone. We understand that people want fewer tools in the toolbox, but duplicating the work and competing on getting the same results with other minifiers seems like a net-negative for the ecosystem. If minification is worthwhile enough, then the extra build step is going to justify the cost over time. In fact, newer tools are so fast that the cost for good minification even lower than it was a decade ago when we first discussed this issue.
When TypeScript came out, it assumed it was the only build tool in the chain, and that other build tools like minifiers had to come strictly before or after it. Nowadays, most modern JavaScript tools operate directly on TypeScript - including some bundlers and minifiers. The kind of JS people are generally writing today, using ES Modules and (maybe) #private members, is also much easier to statically analyze for the sake of DCE/tree-shaking and local identifier renaming, both of which can be done after types are removed from the original code.
We know that there's been a lot of feedback on this issue, and as such believe that there is not much new ground left to cover with further comments on the topic. To prevent a flood of notifications on everyone's inbox, we're temporarily locking this issue for 2 weeks for some pre-emptive cooldown, but further discussion can pick up at that time if needed (2/28/2023).

Is there any update on this?

Update: This is still out of scope

I don't know if there's already a proposal for this (couldn't find in a simple search), but IMO better than minification would be the opposite.
By that I mean an option for TypeScript to make every possible effort to keep JS line numbers equivalent to their TS source line numbers. Sometimes source maps are difficult to set up or just won't be properly supported. It would be great if I could look at a stack trace with a JS line number and go directly to its source on the same line.
So it would be great if TypeScript could:

ensure transpiled code retains line numbers as much as possible (e.g. expand by adding extra lines, collapse by minified one-liners) specially the executable lines
e.g. "minify" in one-line code added at the top (e.g. for compatibility) to avoid changing subsequent line numbers
keep my indentation style in the output (e.g. tabs or 2 spaces)

This is something you can't really do with an external tool (or at least it would be more complicated) I believe.
Is it worth opening a feature request?

This is something you can't really do with an external tool

This would be straightforward-ish to do by using the emitted sourcemaps

originally asked in 2014, we are 2024 now.
as a TypeScript n00b am I correct the tsconfig.json has no option to produce minified javascript output files directly?
We have the option to "removeComments"
@chaser92 suggest " TypeScript should better do what it's best at, one tool should serve one purpose."
I don't agree, it already has options to "minify" the output --> "removeComments", so why not further optimize it with additional options or a generic "minify": true/false ?
Not a fan of having yet another tool to maintain, update, (resolve conflicts- with while this could be in TypeScript itself.
looking from a distance at this this looks like something so obvious and "normal" to have...
Imagine the reduction of traffic and resources this could give on webservers if this would be on by default, globally. 💚 😃