As it should be obvious to anyone following my rants on Twitter, I’ve been playing with TypeScript quite a little bit recently. And although I haven’t created any huge project with it, I have been embracing it little by little on everything I’ve been doing that requires JavaScript.
This past weekend I had free time at home, and although I was not taking part on another Ludum Dare compo, I decided to dedicate some hours to porting KeyActionBinder (the ActionScript 3 library I use for Gamepad support in Flash/AIR) to TypeScript.
This exercise was prompted in part by reading a forum thread discussing the possibility of developing a new version of the Phaser game framework in TypeScript (with a lot of opinions against it). I certainly have my own opinion on the matter – I think creating a JavaScript library in TypeScript is a solution where the pros far outweighs the cons – but I had to admit I didn’t have much experience maintaining an open-source TypeScript project other than my own little helper classes and mini-libraries.
The initial result of this exercise is KeyActionBinder.ts, a complete TypeScript rewrite of the original AS3 class. Granted, the new Gamepad APIs are much less complicated in a browser than in Flash, as the input layout of recognized devices is standardized (you can just read the state of a given button, without caring for which device it is). Indeed, the browsers seem to do a lot of the heavy lifting of managing different input devices. In this sense, an additional Gamepad class isn’t exactly a big necessity, as reading the current gamepad state is trivial. However, I’d still like to have the ability to manage player control in a more abstract way, without having to constantly run hard-coded game input logic in a game loop; this was one of the biggest features of KeyActionBinder, in my opinion.
Enough rambling. The library is created entirely in TypeScript with no external dependencies, but in its main build format – JavaScript – it works like this:
<script src="key-action-binder.min.js"></script>
// Create instance
binder = new KeyActionBinder();
// An action binding
binder.action("jump")
.bindKeyboard(KeyActionBinder.KeyCodes.SPACE)
.bindGamepad(KeyActionBinder.GamepadButtons.ACTION_DOWN)
.setTolerance(0.05);
// An axis binding
binder.axis("speed-x")
.bindGamepad(KeyActionBinder.GamepadAxes.STICK_LEFT_X)
.bindKeyboard(KeyActionBinder.KeyCodes.LEFT, KeyActionBinder.KeyCodes.RIGHT);
// Evaluate the actions inside the game loop
function myGameLoop() {
// Move
var speedScaleX = binder.axis("move-x").value; // -1..1
player.speedX = speedScaleX * maximumSpeed;
// Jump
if (playerIsOnGround && binder.action("jump").activated) {
player.speedY = jumpSpeed;
binder.action("jump").consume(); // Disable existing activation
}
}
It is a slightly different syntax from its ActionScript incarnation, but one I’ve been meaning to experiment with for a while. It separates concerns more clearly; this makes the interface easier, and maintenance less painful.
The important point I’d like to discuss in this post is still TypeScript, however.
Porting this project has been an interesting exercise in learning more on how to deal with the language and the platform, both the bad and the good things. Despite being a small project, I think there’s a few things I’ve had to deal with that are good examples of what one might face with open source projects.
One of them is the need for definitions for elements that are not exactly part of the current browser standards. The Gamepad APIs are great but not widely supported, and as such not a part of the definitions used on TypeScript by default. This was not a big problem; I simply needed to resort to using the DefinitelyTyped definition for the APIs. However, it is one interesting aspect of creating a TypeScript project that is trying to work with the bleeding edge of what’s possible, and one I’ve had to deal before in other TypeScript endeavours.
Other than that, however, everything about the project was a breeze. Advanced features of the language granted me more security in writing the code than I’ve ever had with the ActionScript variant. For comparison’s sake, the SimpleSignal class I used for events was made much more manageable due to the use of Generics; in the ActionScript version of the same class I needed to annotate the code with the parameters my functions actually expected, lest I forget what my events needed.
Overall, while this version of the library is lesser battle-tested, it already supports more features than the original AS3 version (the ability to simulate analog axes with keyboard keys is one of my favorites). Errors due to syntax and misuse of objects are assumed non-existent, and I’m left to create bugs by faulty logic alone.
Additionally, I was running TypeScript as a gulp task service that constantly watched as I saved my files, and recompiling them on the spot. Because of that and the small size of the project, I was able to quickly switch between editor and browser to test without having to actually deal with compilation delays; it always happened invisibly. Most of the time, compiling a new file took less than 100ms, with the full project taking about 1.5 seconds to compile.
I find it a bit funny to compare this experience with this post of mine from 2012, where I talked about the unveiling of TypeScript:
First, using a compiled language takes away some of the advantages of using pure JavaScript – which is testing and debugging in a browser without any kind of additional step needed. Compiled code is great if you’re talking about deploying production code, but less so if you’re still building and testing. I would have preferred a pure ECMAScript 6 compliant browser (if/when an actual standard exists) that allows you to test your code natively; one could then use compilation before deploying, to ensure backwards compatibility until browsers catch up. This could be dampened (and then some) with on-the-fly, server-side compilation, but it’s not very clear to me whether that’ll be possible.
Ah, to be so young and naive. And server-side compilation? What? I don’t think I’d use it today, were it available.
What I didn’t know at the time was that compilation, transpilation, minification or other random steps like that would become a common thing not just in projects using other languages that transpile into JavaScript, but even in pure JavaScript projects. Nowadays, creating the necessary gulp tasks is the second step I perform while setting up a project, right after creating the actual source and build folders. Setting up watch steps so I can develop seamlessly is something that comes naturally. It’s not a big deal. It does not interfere with the development workflow; it actually helps immensely by catching errors earlier.
In that same post, I mentioned some fear and uncertainty about where Microsoft was taking the language. This has been mostly dissipated for two reasons: the language has been making great strides since then, and with Microsoft open-sourcing the language, it’s safe to say there’s a nice exit strategy in case one is needed in the future.
In any case, its the JavaScript output of TypeScript is perfectly maintainable too, in case it’s ever needed.
In the past, I’ve been bit by platforms that were kept under tight control of a corporation. That works well for a while, when the platform is popular. However, once the commercial interest is not there anymore, they tend to wither and die. That history lesson should not be forgotten. But so far, this story seems to be playing out quite differently, and it looks like TypeScript has a bright future ahead of it.
If you’re building anything in JavaScript right now, I’d suggest you take a serious look at TypeScript.