Why Alice Runs JavaScript

Over at The Mad Botter I’ve been working my tail off trying to get a new version of our AI Slack bot Alice out before the end of the new year. As the code has become more complex, I turned to Typescript, Microsoft’s programming language that’s intended to bring static typing and a number of other quality of life improvements to JavaScript. After writing a few classes and a module in Typescript, I switched to pure ES6 JavaScript. Here are my reasons and please do keep in mind that I am writing a large scale bot, so I have some pretty high-end architecture needs.

Classes: As much as I’ve railed against previous efforts to impose classical inheritance and object oriented patterns on JavaScript by efforts such as, Class.js, they’re useful. Alice’s code-base is getting large enough that managing the overall architecture is a problem. For things like data models an object oriented class structure makes a lot of sense as longs as you avoid coupling. So, I went ahead and started writing some Typescript classes such as this simple example:

class Contact {
    fullName: string;
    emailAddress: string;
    profileImageUrl: string;

    constructor(fullName: string, emailAddress: string, profileImageUrl: string) {
        this.fullName = name;
        this.emailAddress = emailAddress;
        this.profileImageUrl = profileImageUrl;
    }
}

Nothing too fancy here. Out of curiosity I took a look at the generated JavaScript for this and was a little surprised:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class Contact {
    constructor(fullName, emailAddress, profileImageUrl) {
        this.fullName = name;
        this.emailAddress = emailAddress;
        this.profileImageUrl = profileImageUrl;
    }
}
exports.Contact = Contact;
//# sourceMappingURL=contact.js.map

WOW! That looks basically the same? Couldn’t I just write that and not have to deal with the complexity of having an dist folder full of generated code? Yes, I can, so it seems ES6’s classes have done a pretty good job of mitigating that need from Typescript.

Static Typing: I can feel some of already drafting your tweets pointing out that in the vanilla JavaScript version of the code, I lost all the types. That’s true, but I’m not really sure I care. Sure, static typing has benefits, such as empowering the compiler to catch a number of common programming errors. There are ways around this issue; you could always do what Ruby developers by replacing the compiler type checking with a bunch of unit tests that do little more than check types; sorry, couldn’t help myself there. If you really want the type of static typing protection that Typescript offers, then that might be a reason to stick with Typescript, but I’d urge you read this great post by Eric Elliot before you fully throw down your gauntlet in favor of static typing as a must have.

Debugging: Debugging Typescript just doesn’t work the way I want it to in Node. When an exception is thrown, the line referenced in the exception is not the Typescript code the developer wrote but rather the generated JavaScript code. That’s pretty terrible. The workflow seems to be something like this: you look at the exception in the JavaScript code and have to extrapolate back to the Typescript you wrote and figure out what exactly is causing that behavior. In small code-bases that might not be too hard, but at scale this doesn’t make a lot of sense.

For me, the primary benefit of Typescript ended up being classical OO and the debugging issue became too much of a nuisance to tolerate. I am sure that there is some awesome project out there that I could have integrated into my work-space that would have at least mitigated the debugging issue, but that would again add more complexity to my tool-chain and possibly to my staging and production environment. Let me know what you think in the comments or on Twitter.