Repository navigation
Suggestion: minification #8
Description
Activity
I think that this isn't the best idea - TypeScript should better do what it's best at, one tool should serve one purpose. There are a lot of great minifiers out there.
Reacted by Agustin Chiappe Berrini, Paruyr, Vinicius Dias, David Domingo, Eric, Sebastiaan Dammann, Hadrien Milano, Max, Felipe Gonçalves Marques, Kris Kaczor and 65 moreReacted by Костя Третяк, Thomas, 柴树杉, Fis, Taro, Matthew McEachen, Leonardo Souza, Rubén López, Thomas Devries, Lowell Bateman and 263 moreReacted by oxygen, Daniël, Aykut Yilmaz, Yaksh Bariya, Yves M. and Ricardo Fernández SerrataReacted by Daniël, Henry and shenleban tongyingMariusz Kierski (@chaser92), this request is to minify TypeScript code, not JavaScript code. A minifier that uses the information (primarily information on access modifiers) available to the TypeScript compiler will be much more efficient than any JavaScript minifier out there.
I am personally very eager to see this implemented.
Reacted by Jay Querido, Khalid Jebbari, biwin, Jeremi Stadler, Flavien Volken, Wojtek Bednarzak, Yassel Avila, Tamás Hegedűs, Luiz Sa, Ilia Choly and 288 moreReacted by Thomas Richards, Trotyl Yu, Rado Kirov, fregante and Abdelkarim Ben HamidaReacted by Ricardo Fernández Serrata and prRyanCavanaugh commented
on Aug 7, 2014 MemberAuthorMore actionsMotivating 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.
Reacted by Steven, Peter Hultqvist, Lester Sy, Gark Garcia, Sam Claus, ExE Boss, fregante, minhducsun2002, Mark Jones, Miles B Huff and 8 moreThe following TypeScript code
class Greeter { private greeting: string; constructor (message: string) { this.greeting = message; } public greet() { console.log(this.getMessage()); } private getMessage() { return "Hello, " + this.greeting; } }
Can be compiled into JavaScript that when run through an external minifier results in the following:
var Greeter = ( function () { function a(b) { this.greeting = b } a.prototype.greet = function () { console.log(this.getMessage()) }; a.prototype.getMessage = function () { return "Hello, " + this.greeting }; return a } )();
Problems:
- The private field
greetinghas not been minified. - The private method 'getMessage` has not been minified.
- The type name "Greeter" has been mangled into "a". This breaks code such as
(/function (.{1,})\(/).exec((instance).constructor.toString())for obtaining the type name at runtime. - The constructor parameter "message" has been mangled into "b". This breaks code that attempt to perform dependency injection by parsing constructor arguments.
In summary, even in just this simple snippet of code TypeScript can improve on an external minifier, both by mangling names that should have been minified as well as optionally leaving names untouched.
Reacted by Jay Querido, biwin, Ilia Choly, Kevin Cox, Arsène von Wyss, John Weisz, Jed, Jan Janoušek, acrazing, james gilles and 110 moreReacted by fregante and Denis LepyohinReacted by Yiu, Alex Ewerlöf, Jesse Lee, Raphael, DoimoInterlogica, Om Mahesh, Andreas and prReacted by Yves M.- The private field
RyanCavanaugh commented
on Aug 11, 2014 MemberAuthorMore actionsIf I use the Closure Compiler, it goes from code like this:
var Greeter = (function () { function Greeter(message) { this.greeting = message; } Greeter.prototype.greet = function () { console.log(this.getMessage()); }; Greeter.prototype.getMessage = function () { return "Hello, " + this.greeting; }; return Greeter; })(); var x = new Greeter(); x.greet(); console.log(x.getMessage());
to this:
var b = new (function() { function a(a) { this.c = a; } a.prototype.b = function() { console.log(this.a()); }; a.prototype.a = function() { return "Hello, " + this.c; }; return a; }()); b.b(); console.log(b.a());
Here, the
greetingfield andgetMessagenames have been correctly minified. The "over-minification" of other variables leads to my next discussion point on this.Some people think class names, property names, parameter names, local names, etc are meaningful runtime metadata, other people think they're not. Some other people think only some of their property names, parameter names, local names, and so on are things that should be minified. Among those people, there's disagreement over how those names should be specified (globally? locally? in the code? in a config file? on a commandline?). Nearly everyone believes that their set of rules is the only one that makes sense.
The only path forward that could beat existing minifiers also involves a ton of configuration for what the allowed set of minifications even is; implementing and testing all these rules would be very expensive. It's a lot of investment for what is probably a few percent improvement (especially after gzipping) over the state of the art here. That's why we want to see compelling examples for where only the TypeScript compiler could minify and do a meaningfully better job than any existing tool.
Reacted by garkin, Seth Brenith, Pavel Gurecki, mjason3, Tim van der Lippe, xiongjzh, Sam Claus, fregante, v1rtl, LenaWil and 18 moreRyan Cavanaugh (@RyanCavanaugh),
Regarding the under-minification problem, it looks like the code in your post was minified using the "advanced" option of the closure "compiler". And yes, that appears to solve the problem in this simple case. However, consider this slightly more involved example:
class Greeter { public greet() { console.log('greet'); } } function createInstance<TType>(name:string): TType { return new window[name](); } var x = createInstance<Greeter>('Greeter'); x.greet();
Basically, we have introduced a class factory function. The closure compiler, with the advanced option, minifies this to
(function() { function a() { } a.prototype.a = function() { console.log("greet"); }; return a; })(); (new window.Greeter).a();
Clearly this is going to fail at runtime. The solution is to export the "Greeter" symbol:
window["Greeter"] = Greeter;
Seems simple enough. But in large projects with hundreds of classes, this is not trivial. TypeScript understands this code better, because it knows that
function Greeteris a class name.More importantly, the problem I have with the closure compiler is that it requires the entire code-base to be minified in one go. This is not feasible in large projects, where there is separation between library code and client code. In this situation it is necessary to declare externs, which ultimately results in maintaining one's public API in duplicate.
With regard to the second issue that was raised, namely the problem of configuration, that, I guess, is an implementation detail: if there is demand for configuration then that would have to be dealt with. But it seems strange to decline an entire issue on that basis.
A possible halfway solution would be for TypeScript to provide an option to generate closure-style extern files or to export symbols (class names).
Reacted by Stronger, Peter Hultqvist, Michael Coxon, edA-qa mort-ora-y, Chayim Refael Friedman, Kyle Agronick, Alistair Smith, Om Mahesh, gscanlonhq, groege and 2 moreThe configuration is not just an implementation detail. The fact that we can go back and forth all day with different code examples that need differing amounts of minification is proof of the need for either a) very simple minification algorithms b) extremely customizable minification options. You've already identified multiple issues that absolutely demand customization to work at all.
In addition, we're still talking about this nebulous concept of 'the TypeScript compiler understands the code better so it should just do this example correctly' (for some definition of correctly). There's still very little here that is actually stating proposed solutions to classes of problems which you could put a configuration over the top of.
To summarise the discussion so far:
- The JavaScript generated by TypeScript is not optimal in terms of providing that output as input to a simple minifier. This is largely due to private methods going on the prototype. A developer wanting to write code that is optimal for a minifier would have written those private methods as simple functions within the class closure.
- This problem can be solved by running the output through an advanced minifier (for example the closure compiler with the "advanced" option).
- Unfortunately the closure compiler requires all symbols to be within sight of the minification run. If they are not then those symbols must either be exported (added using bracket notation) or an externs file must be created, declaring the symbols that should be preserved.
- User preferences for minification can be diverse.
- The TypeScript compiler has a superior information set with which to perform minification than a JavaScript minifier; this information set includes having access to the following modifiers "class", "private", "public", "constructor" and the API of external libraries through their declarations files.
- The biggest advantage that a TypeScript compiler has over ordinary minifiers is being able to safely perform minification on any subset of the code-base. By "safely" I mean that, firstly, exported classes and their public API can be preserved, and, secondly, calls made from within a class into external libraries can also be preserved with recourse to the information in their respective declarations files.
In the light of the above, I have three basic proposals listed in order of preference:
A. A fully functional minifier provided by TypeScript.
We provide two compiler options--minify-simple- Preserves class names, constructor signature...--minify-advanced- Performs a more aggressive minification.
In both cases, private fields and methods would always be minified.
B. TypeScript generates externs files for providing to the Closure Compiler if specified.
C. TypeScript only minifies private fields and methods if specified.
It is conceivable that with option A there will be many people who would like a halfway solution between "simple" and "advanced" minification, e.g. preserve class names but minify constructor signature. In this instance, it may be possible for those concerned to run the output generated by TypeScript through a third-party minifier that provides more specific options.
Reacted by Gark Garcia, Gerardo Lima, Sandy F, ExE Boss, Eran, Davi-Danger, maki, Ugur KAZDAL, Roman, Michael Updegraff and 14 moreTL;DR
Perhaps Data-Annotations could be added to Typescript's syntax to provide developers with an easy way to tell the compiler what should, and should not be overly minified.I have been writing a large Angular app using Typescript. I've been painfully watching the size of the JS grow and grow, and would personally LOVE to have some advanced compilation support that only Typescript can provide. IMO Typescript can do an incredibly good job at minifying code, better than closure because you're not limited to comments for specifying compiler directives.
For an Angular app, there needs to be a high level of granularity of what functions and properties are minified (a la Closure Compiler's advanced mode) and what properties should be left as-is. They are referenced in the HTML and oftentimes used by Angular to correctly link the DOM to JS. You'd need a solution on an item by item basis in order to accomplish this in a way that would make it easy to use.
For example, much in an Anuglar directive can be truly 'Javascript Only', and I would like advanced minification on it. However, there are some variables that need to be exposed to HTML, outside of the javascript engine.
If we were able to use something similar to a C# data-annotation on class properties, that would be a much better method than Closure compiler's recommendations. I've seen a lot of Javascript written for the Closure compiler that uses array notation to reference unminified properties - This is a pain to write. Any time you reference a property by a string in Javascript, it just feels muddy to me.
I've been using Newtonsoft.Json on a dot Net backend. We directly serialize our business logic models to the client side - Of course, we want to keep some things hidden from JSON serialization. For those without a C# background, a data annotation looks like this:
Imports Newtonsoft.Json class Klass { [JsonIgnore] public string[] SomethingVeryLarge; public string SomethingMoreManageable; }The [JsonIgnore] data annotation instructs Json.Net to overlook this property when parsing an instance of Klass.
Having something like data-annotations could provide the compiler with a really good system of flags, that could be used for this advanced minification support, or other compiler features. I could see this syntax eventually being usable Typescript programs to further extend the language.
there are some variables that need to be exposed to HTML, outside of the javascript engine.
Yes, that's a relevant point as well. This is also the case when using KnockoutJS, for example:
class ViewModel { [minifyIgnore] public foo = ko.observable('bar'); }
because
foois referenced in the HTML:<div data-bind="text:foo" />
I would love a typescript aware minifier. Also, because I've had some issues with
ng-minnot working very well ontscgenerated javascript. (I haven't triedng-annotateyet)Reacted by pj, Matthew Gamble, Jason Leo, groege and Maximilian ThornI Agree with Ryan Cavanaugh (@RyanCavanaugh)
I'm working for a Open Source HTML5 Game Framework called EGRET
Many of our developers thinks that ourgame.min.jsshould be smaller and smaller .
Now we compress ourgame.min.jswith Google Closure Compiler SIMPLE_OPTIMIZATION , there are some reason we abandon ADVANCED_OPTIMIZATION- It's very hard to use ADVANCED in a complex project with Zero bug . We had to test the whole the test case carefully again . When we turned to SIMPLE mode , it has too many private long-named field not be optimized , C is a useful solution with SIMPLE mode
- We should support our customer a
lib.min.jsand there are many public api inside . But ADVANCED_OPTIMIZATION delete almost all the apis because it thinks that the apis not be used . To solve this problem , we should provide aextern-fileto Closure Compiler , but we have to many API . B solution with ADVANCED mode is a good idea , because oftsccan generate the extern files very easy ( such as .d.ts )
So, I hope both B and C could be added to
tsc.
By the way ,forgot A ,please ^_^176 remaining items
Load more actions- addedOut of ScopeThis idea sits outside of the TypeScript language design constraintsThis idea sits outside of the TypeScript language design constraintsand removedNeeds ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.This issue needs a plan that clarifies the finer details of how it could be implemented.VS Code TrackedThere is a VS Code equivalent to this issueThere is a VS Code equivalent to this issue
on Feb 14, 2023 RyanCavanaugh commented
on Feb 14, 2023 MemberAuthorMore actionsAfter 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)
#privatemembers, 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).
Reacted by Carlo, Max Patiiuk and Amit BeckensteinReacted by Shinigami, Pedro Augusto de Paula Barbosa and Alexander Ozhigin- locked as resolved and limited conversation to collaborators
on Feb 14, 2023 - unlocked this conversation
on Feb 28, 2023 Is there any update on this?
Reacted by Sean Genabe, Holger Jeromin, Shinigami, Kitson Kelly, Ryan Cavanaugh and 7heMechRyanCavanaugh commented
on Mar 15, 2024 MemberAuthorMore actionsUpdate: This is still out of scope
Reacted by Holger JerominI 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?
RyanCavanaugh commented
on Apr 17, 2024 MemberAuthorMore actionsThis 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"Mariusz Kierski (@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. 💚 😃
Reacted by Katarn- locked as resolved and limited conversation to collaborators
on Oct 15, 2024 - marked minify when compile #62165 as a duplicate of this issue
on Aug 1, 2025
TypeScript should support emitting minified JavaScript.
There are several different things we could support: