Tuesday, November 27, 2012

Various TypeScript Weirdnesses

I’ve been playing around quite a bit with TypeScript lately. I’m no fan of JavaScript – Douglas Crockford is crocked, IMO – so the idea that it might be possible to fix its enormous problems while retaining its real strengths has a whole lot of innate appeal to me. In addition, the fact that Anders Hejlsberg (the genius behind C#) is also behind TypeScript gives the project some immediately credibility.

I also saw an immediate use for it. I’m currently in the process of rewriting Alanta’s API, and during the first two iterations of the API, I struggled no end with getting JavaScript to act like a reasonable, modern language. It’s sort of possible, but given JavaScript’s extremely extensible nature, the potential for the tools to help you the way that they help you in C# or Java (or even C++) is pretty limited. You can force JavaScript into supporting things like inheritance and polymorphism, but it’s not pretty, and it’s easy to make mistakes.

So when it came time to start work on V3 of Alanta’s API, I decided to take the dive, and rewrite it all in TypeScript. I’m a couple thousand lines into it so far, and my initial conclusions have four parts:

(1) TypeScript and the tools around it are still pretty buggy.

Here’s an example. With VS2012 and version 0.8.1 of the TS compiler and tools, try typing this into a TS file:

class A {}
class A extends A {}

It’s nonsense code of course, but it’ll also hang VS2012+TS 0.8.1 hard.

(2) TypeScript still doesn’t have many of the things you’d expect from a modern language.

Generics are the big one here. You can get strongly typed arrays, which is kinda helpful:

class Animal { }
class Dog extends Animal { }
class Plant { }
var animals: Animal[] = [];

// These work
animals.push(new Animal());
animals.push(new Dog());

// This won’t compile
animals.push(new Plant());

But you can’t (yet) do something like this:

class ViewModelBase<TModel> {

    private model: TModel;
    private callbacks: { (model: TModel): void; }[] = [];

    constructor (model: TModel) {
        this.setModel(model);
    }

    setModel(model: TModel): void {
        this.model = model;
        this.raiseNewModel();
    }

    onNewModel(callback: (model: TModel) => void ) {
        this.callbacks.push(callback);
    }

    private raiseNewModel() {
        for (var i = 0; i < this.callbacks.length; i++) {
            this.callbacks[i](this.model);
        }
    }
}

Nor is there support for “async/await”, like the latest release of C#, nor any support for XML Doc or jsDoc comments, or extension methods, or protected methods, or even conditional compilation. And lots and lots of others. But I suspect that most of those will come in time; and of course, with a few caveats, JavaScript doesn’t have support for any of these either, right?

(3) TypeScript has some really weird, unexpected behaviors

This is probably my biggest issue with the language so far. Some of these behaviors are presumably “as-designed”, some might be actual bugs, and it’s not unreasonable to expect that many of them will change before the language is officially released. But they still represent some significant “gotchas” when you’re first getting used to the language as it stands right now.

One example is the weird behavior of the “this” keyword. If you’ve done any web coding at all, you know that “this” in JavaScript refers not to the class in which the method is defined, but to the object to which the method in question has been assigned. Given that JavaScript doesn’t have native support for classes, this sort of vaguely makes sense, but TypeScript is just different enough that you’re likely to get confused all over again.

For instance, take a look at this bit of code below.

class Foo {
    constructor () {
        document.onmousemove = this.showMessage;
    }
    message: string = "Hello";
    showMessage(e?: MouseEvent) {
        console.log(this.message);
    }
}

var foo = new Foo();
foo.showMessage();

Calling “foo.showMessage()” works as you’d expect. But when exactly the same method is called from the “document.onmousemove” handler, “this” gets assigned to the global “document” variable, and as a result “this.message” is undefined. That’s pretty close to how JavaScript acts, but not how “this” behaves in any other class-based language I know of. You wouldn’t normally expect a class method to exhibit entirely different behavior, depending on how it’s called. The workaround for it, by the way, is a tad odd, if handy: just assign the event handler like this:

document.onmousemove = e => this.showMessage(e);

Here’s another example. It turns out that TypeScript has a strange way of initializing methods and fields. Basically, by the time you get around to calling object constructors, you can depend on methods to have been overridden correctly, but you can’t depend on fields. For instance, take a look at this code:

class User {
    constructor () {
        console.log("Field from: " + this.field);
        console.log("Method from: " + this.method());
    }
    field: string = "User class";
    method(): string { return "User class"; }
}

class RegisteredUser extends User {
    field: string = "RegisteredUser class";
    method(): string { return "RegisteredUser class"; }
}

var registeredUser = new RegisteredUser();

In my opinion, it would make most sense to have this output:

Field from: RegisteredUser class
Method from: RegisteredUser class

Failing that, this would at least be consistent:

Field from: User class
Method from: User class

But instead, this is what we get:

Field from: User class
Method from: RegisteredUser class

And of course, that’s not at all intuitive. In looking at the emitted JS, it’s clear why this happens: methods are initialized (i.e., assigned to the class prototype) when the class is constructed, but fields aren’t initialized until the object constructors are called, and those get called in the order superclass->subclass.

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
var User = (function () {
    function User() {
        this.field = "User class";
        console.log("Field from: " + this.field);
        console.log("Method from: " + this.method());
    }
    User.prototype.method = function () {
        return "User class";
    };
    return User;
})();
var RegisteredUser = (function (_super) {
    __extends(RegisteredUser, _super);
    function RegisteredUser() {
        _super.call(this);
        this.field = "RegisteredUser class";
    }
    RegisteredUser.prototype.method = function () {
        return "RegisteredUser class";
    };
    return RegisteredUser;
})(User);
var registeredUser = new RegisteredUser();

So when you call the RegisteredUser() constructor, the correct methods have already been wired up correctly to its prototype, but the fields haven’t been: so the User() constructor calls the right methods, but doesn’t call the expected fields. That’s understandable when you look at the emitted JavaScript, but not at all intuitive. Basically it means that the same exact field reference in the same method can sometimes be referring to a field from the local class, and sometimes to a field from the subclass, depending on where you’re calling it from. That’s a pretty serious violation of the “law of least astonishment”.

Even more confusing, however, are some weirdnesses having to do with module loading. There are several different ways to handle dependencies between modules. One way is to just specify them at design-time in a ///<reference /> tag. So if you had a User.js file that looked like this:

class User {
    name: string;
    createdOn: Date;
}

You could then have a RegisteredUser.ts file that looked like this:

///<reference path="User.ts" />
class RegisteredUser extends User {
    registeredOn: Date;
}

But then you have to include both “User.js” and “RegisteredUser.js” in different <script> tags on your web page – which isn’t what you want to do if, say, you’ve got dozens of files and you’re trying to provide an API for lots of external users to use and you’re almost certainly going to be refactoring and changing the module names regularly.

The other way is to use named modules. The way you do this is a little odd if you haven’t worked with JavaScript loaders like tiki (which uses the CommonJS format) or curl and RequireJS (which use the somewhat more complicated and flexible AMD format). You’d modify your “User.ts” file by adding an “export”:

export class User {
    name: string;
    createdOn: Date;
}

And then you’d modify your RegisteredUser to import that module, which then acts as a sort of namespace prepending the exported “User” class:

import mUser = module("User");
export class RegisteredUser extends mUser.User {
    registeredOn: Date;
}

Assuming you’re using the AMD format, the compiled code for User.js looks like so:

define(["require", "exports"], function(require, exports) {
    var User = (function () {
        function User() { }
        return User;
    })();
    exports.User = User;    
})

And the compiled code for RegisteredUser.js:

var __extends = this.__extends || function (d, b) {
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
define(["require", "exports", "User"], function(require, exports, __mUser__) {
    var mUser = __mUser__;

    var RegisteredUser = (function (_super) {
        __extends(RegisteredUser, _super);
        function RegisteredUser() {
            _super.apply(this, arguments);

        }
        return RegisteredUser;
    })(mUser.User);
    exports.RegisteredUser = RegisteredUser;    
})

All of those “define” calls look weird, but it basically means that the module loading gets offloaded to (say) RequireJS. But if you want to use any of these classes on a web page, there’s another step you have to go through, which is to require() them on the web page itself, like so:

<script type="text/javascript" src="../Scripts/require.js"></script>
<script type="text/javascript">
    require(['User', 'RegisteredUser'], function (mUser, mRegisteredUser) {
        var user = new mUser.User();
        var registeredUser = new mRegisteredUser.RegisteredUser();
    });
</script>

If you’re used to a nice, clean build and dependency system like C# gives you, all that’s a bit much to wrap your head around. And you have to jump through a few more hoops if you want to give users of your library a decent experience. But it’s not the weird part. The weird bit comes when you start trying to mix all this module loading stuff with interfaces. Specifically, this is  a problem I ran into when I was working with trying to get SignalR working with TypeScript. I had a “Service.ts” file that looked something like this:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

interface SignalR {
    roomHub: Service.RoomHub;
}

module Service {
        export var roomHub = $.connection.roomHub;
        export interface RoomHub { }
}

And that worked fine. But then I needed to “modularize” it, so that I could access it from other files, so I added an “export” on the module bit, like so:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />

interface SignalR {
    roomHub: Service.RoomHub;
}

export module Service {
        export var roomHub = $.connection.roomHub;
        export interface RoomHub { }
}
And suddenly the compiler informed me that “roomHub” wasn’t a member of “$.connection.roomHub”. I’m honestly not sure if this is a compiler bug, or some expected but entirely unintuitive side-effect of modularization. And it took me quite a while to figure out the workaround: to move my interface definitions into a separate file (“ISignalR.ts”):
interface SignalR {
    roomHub: RoomHub;
}

interface RoomHub {
}

And then reference that file from the file that contains the exported Service module:

///<reference path="../Scripts/jquery-1.8.d.ts" />
///<reference path="../Scripts/signalr-1.0.d.ts" />
///<reference path="ISignalR.ts" />

export module Service {
    export var roomHub = $.connection.roomHub;
}

Apparently the rule is something like: if your file exports anything, you can’t have any interfaces in it that extend interfaces that don’t originate in the file with the exports. I’m not sure if that’s precisely it, or if it makes sense to have that requirement – but I can’t seem to make it work any other way. (And trust me, I spent hours trying.)

(4) TypeScript is still way, way better than JavaScript

So TypeScript is very new and fairly raw, with some significant rough edges. But there’s still no doubt in my mind that it’s a much, much, much better language than JavaScript. I haven’t experimented enough with CoffeeScript or Dart to be able to speak intelligently about how it stacks up to those alternatives. But at this point, I really can’t imagine going back to normal JavaScript for web development. Why would anyone? And when it finally catches up to languages like C# (or maybe even brings in some functional features from F#) – man, web development might actually start being fun.

Sunday, November 4, 2012

The Dying Rat

Each Fall the maples in our yard prepare
themselves to meet the coming frost and rain
by pulling off their leaves, until their bare
stark branches line the sky, their plain

black limbs all awkward while the bright red
clothes they wore last week with almost an
unseemly pride lie fallow and discarded.
My wife dislikes such garments on the lawn

as much as on the floor. So she had found
a rake last Sunday morn, with church some time
away, and soon the scratching, earthy sound
had called us out of doors as well, to climb

the bank and join her on the muddy grass.
I watched as Caedmon fought his brother for
the rake, and felt a cold that could not pass
the woolen lining of the coat I wore.

Galena was the first to see the rat.
I need to note that her maternal heart
has little room for rodents. A mouse a cat
has caught, the rats that scatter in the dark

into the woods, arouse in her a spring
of primal angst. It quite surprised her there
in all its warm brown loveliness. The thing
was tremulous and soft and strangely fair.

And it was dying. We could see this clear.
Its feet could move, its eyes could see, its head
and ears would shift around when we drew near.
But it was dying. It would soon be dead.

With instincts that were true to all they knew,
my children gathered close to see it breathe.
Its chest would rise and fall; the almost blue
gray fur would ruffle in the wind that wreathed

a mist of falling leaves about us. God
is here, I thought. The God that sees the fall
of each brown bird must know this rat. In awed
and silent concentration, lit by all

the much they knew of love and less of death,
my children watched the slowly dying rat.
And as it struggled for each silent breath,
I sensed the God whose death killed death at last.