TypeScript code generation for object properties

Topics: General, Language Specification
Jan 29, 2013 at 9:58 PM
Edited Jan 29, 2013 at 10:01 PM

Consider the following TypeScript:

 

class Test {
    foo: string;
    bar: string;		
}
	
var a = new Test();
a.foo = 'foo';
//...10000 lines later
a.bar = 'bar';

 

This makes the following JavaScript:

 

var Test = (function () {
    function Test() { }
    return Test;
})();
var a = new Test();
a.foo = 'foo';
//...10000 lines later
a.bar = 'bar';

 

The generated Test "class" does not contain definitions for foo or bar. They are essentially created at runtime. I know that in modern browsers the Just-In-Time-Compilers try to ascertain the shape of objects and allocate memory based on these presumed object types. If the type of the objects appear to change at runtime, the browser discards its assumption and has to dynamically figure out the shape of the object as it mutates.

Correct me if I'm wrong, but in the code TypeScript generates, the object shape is not being defined until its properties are accessed. Isn't this preventing the JIT from accurately precompiling the code?

Wouldn't this be more efficient:

 

var Test = (function () {
    function Test() { 
        this.foo = undefined; 
        this.bar = undefined; 
    }
    return Test;
})();
var a = new Test();
a.foo = 'foo';
a.bar = 'bar';

 

?

Thanks for any insight.

Jan 30, 2013 at 11:46 AM

I believe that (at least for the V8 engine) it is the order in which the properties are set that makes it possible to reuse existing "shadow/hidden class" definitions. If you do set the properties in the constructor or later in the code makes no difference as long as the order is identical every time. At least that is my understanding.

Of course setting them as soon as possible in the constructor makes it easier to guarantee this order (but also causes some additional memory usage). One way to achieve the initializations in the constructor: 

class Test {
  i:string=undefined; // Now typescript will set them in the constructor

}

P.S I'm not sure this type of optimization is worthwhile the effort for the average application. Yes is makes a difference, but if you then use a generic framework like jQuery, it all becomes less relevant compared to some of the overhead such a framework introduces. 

// Peter

Jan 30, 2013 at 1:51 PM
Edited Jan 30, 2013 at 1:51 PM

You're right, for most applications it probably won't make a significant difference. But for an application that say, is creating thousands of objects over and over again, where sometimes properties are assigned and sometimes not, it might make a big difference. It just seems fundamentally odd that TypeScript wouldn't be doing anything to assist the browser JIT in creating type definitions. 

Jan 30, 2013 at 3:12 PM

 

The fact that TypeScript doesn't create property declarations, unless initialised, has implications for the runtime behaviour of the compiled JavaScript when running in strict mode.  For example:

"use strict";

class Foo {
	bar: string;
}

var foo = new Foo();

Object.seal(foo);

foo.bar = "All will be revealed"; 
//Runtime Error: Can't add property bar, object is not extensible

When I first came across this my initial reaction was to raise ticket. However, after working with TypeScript the last few months, the problem has been cast in a different light.

The point of course is that Object.seal is a runtime check and has all but been made redundant by the compile-time safety that TypeScript provides. While previously we had Object.seal sprinkled liberally over the code-base it's increasingly become clear that there is little added value from doing that.

So yes, there is a runtime implication, but no: that may not be a compelling reason for changing the way TypeScript compiles into JavaScript.

There is a benefit from doing that for client-side (browser) JavaScript because of the smaller file size.

Noel

Jan 30, 2013 at 3:20 PM

This is another great argument as to why properties should be initialized to undefined (or null) if not explicitly initialized. If you control the environment your code runs in the compile-time guarantees might be enough, but the fact is by no longer using Object.seal you're losing runtime guarantees about type safety that you used to have.