Thursday, September 17, 2015

TypeScript Private Constants and Minification

I'm currently working on a TypeScript app that has a lot of private static values inside the classes. They don't need to be visible to the outside world, but making a variable "private static" doesn't mean that it will really hide the variable in the compiled JavaScript. It's merely a hint to the compiler to limit access to the insides of the class. Here's an example:

module TestMod {
    export class TestClass {
        private static privateStatic = "I'm private and static";
    }
}

Compiles to:

var TestMod;
(function (TestMod) {
    var TestClass = (function () {
        function TestClass() {
        }
        TestClass.privateStatic = "I'm private and static";
        return TestClass;
    })();
    TestMod.TestClass = TestClass;
})(TestMod || (TestMod = {}));

As you can see privateStatic is completely accessible outside the object after compilation. If you're writing a library that may be used by JavaScript this isn't good (but I probably wouldn't write a JavaScript library in TypeScript). For me the biggest implication of this is that when you minify your JavaScript those members' names can't be modified. The minifier doesn't know that private static variables will never be accessed from the outside.

After running it through a minifier you get this:


var TestMod;!function(t){var a=function(){function t(){}return t.privateStatic="I'm private and static",t}();t.TestClass=a}(TestMod||(TestMod={}));

As you can see privateStatic was not renamed to a shorter name as you might have wanted.

One way we can remedy this is to move the private static variables up into the module. As long as you don't mark them with the export keyword they will be completely hidden from the outside world.


module TestMod {
    var _privateStatic = "I'm private and static";
    export class TestClass {
        public foo() { return _privateStatic; }
    }
}

Compiles to:
var TestMod;
(function (TestMod) {
    var _privateStatic = "I'm private and static";
    var TestClass = (function () {
        function TestClass() {
        }
        TestClass.prototype.foo = function () { return _privateStatic; };
        return TestClass;
    })();
    TestMod.TestClass = TestClass;
})(TestMod || (TestMod = {}));

Now _privateStatic is assigned to a variable in a closure. If we look at the minified JavaScript you can see that _privateStatic has been changed to "n" giving you better minification.

var TestMod;!function(t){var n="I'm private and static",o=function(){function t(){}return t.prototype.foo=function(){return n},t}();t.TestClass=o}(TestMod||(TestMod={}));

One problem with this technique is readability. Notice that I change the name from privateStatic to _privateStatic. I prefix the variable names with an underscore to make it clear that they are defined outside the class but are still private.

If you have a lot of static variables or constants in a class and you're looking to save some bytes in your minified application you might take a look at using this technique. It may make your code a little harder to read but the trade off is you get a smaller app. 

No comments:

Post a Comment