Related tags

Writing Backbone Applications in TypeScript

08 September 2017

Nowadays JavaScript evolves fast. We have ECMAScript 6th and 7th editions released over just 2 years. the current support of ES6 specification in major browsers is close to 100%. So the language changed, and changed for better. It has become more expressive, much more concise and readable. It is tempting to jump into, but what about older browsers? Transpilers can translate ES.Next into the old-good JavaScript of ECMAScript 5th edition.

Then the question is which transpiler to pick up? Babel (https://babeljs.io/) is still on the rise, but look what is happening to TypeScript. Since it was chosen for Angular2 it is gaining its momentum. Why have they decided for TypeScript? Because it's much more than transpiler. TypeScript enhances JavaScript with optional features such as static typing, interfaces, abstract classes, member visibility and more. That's a completely new level, not available in plain JavaScript. For example if have a simple function accepting a string for trimming:

function trim( text ) {
return text.trim();
}

If we accidently pass not a string it throws an obscure TypeError

trim( 1 ); // TypeError: text.trim is not a function
trim({}); // TypeError: text.trim is not a function

It's even worse if the function accepts wrong typed arguments. It may product in a not-intended result, what can be hard to trace. In both cases we are forced to extend the function with lines and lines validating the input.

On the contrary with TypeScript we can simply add the type constraints on both entry and exit points:

function trim( text: string ): string {
return text.trim();
}

As soon as you dare to give a wrong type you get warned, already on the IDE level.

Thus we can establish strict interfaces and afterwards TypeScript takes care that they are not violated. In addition to consistency it saves enormous amount of time on debugging. We get immediately informed where exactly and what we did wrong.

It's all nice, but being a Backbone developer you may think of how it may help you. It may indeed. TypeScript is quite friendly to external libraries. You just need to enable the corresponding typings that can be found in public repository https://github.com/DefinitelyTyped/DefinitelyTyped

Setting up the development environment

First of all we need some setting up. If we do not have TypeScript compiler we install it with npm:

npm install -g typescript

We will need also typings tool to manage TypeScript definition dependencies, e.g. for Backbone

npm install typings --global

Now we can create a sandbox directory, enter it and install the typings:

We ask here TypeScript to convert our code in ES5 (an old edition currently supported by almost every user-agent). We state that our modules to translate into CommonJS and to resolve in Node.js style. At last we require TypeScript to output the generated ES5-code into build directory and to start app/bootstrap.ts module. It shall also include the installed typings (./typings/index.d.ts).

Well, TypeScript will look for bootstrap.ts in app directory. So we have to place there our first demo module. For example:

Remember TypeScript is required to produce CommonJS modules. Here we load them asynchronously with SystemJS, but we could bundle the modules into a single file with Browserify and load it as script element source.

At that point our working directory may look like that:

Everything is ready, so we compile our app/bootstrap.ts:

tsc

It compiles our source into build/bootstrap.js and in this case simply coping the content of bootstrap.ts. As we request index.html from a browser we find in the JavaScript console the desired Hello world!

We declare a view class that inherits from Backbone.View. By the requirements of backbone-global typings we have to explicitly specify the model class, which the view accepts. In this case we use no model so the generics may have Backbone.Model. The class consists of two public methods (initialize and render) and a static property tpl. As you might noted the short function notation doesn't require keyword function. When visibility modifier (public, private, protected) is omitted the member is considered as public. You know the method initialize Backbone calls during construction. So we simply require our view to render automatically. And for the rendering it replaces the bounding element's innerHTML with content of tpl property.

Note, that we've used here Template Literal. It simplifies the assignment of the multiline string plus evaluates expressions within it.

But what if we want to reference the options passed to the view constructor? The most obvious way to achieve it is declare a property and set during construction.

Here we compile our template with _.template into this.template. On rendering we use the last one to build a new state of view based on the current content of the collection.

Note that we've used fat arrow function notation for the success handler of the collection fetch method.

success: () => {
this.render();
}

The reason was not about shortening the code (with short function notation it could be even more concise). Fat arrows functions maintain the outer context, meaning within the function body we can courageously refer to this knowing it's our HeroView instance.

Method onSelect receives an argument of type Event that has a property of type EventTarget. So you cannot access for example e.target.value as this property doesn't exist in the interface. But we know that our EventTarget is always a select control therefore of HTMLSelectElement type. So we have to inform TypeScript about it.

Another interesting topic regarding ES6 and TypeScript is destructuring. When we have an array-like object we can destructure it into a list of variables as easy as that:

It's an array of PowerModelJson types where every element described with the corresponding interface:

interface PowerModelJson {
item: string;
}

So that's an object literal with one property item of type string.

What's left to do is to change the type of th view template property:

private template: Template;

Since now on an attempt to pass invalid type TypeScript warns us

Afterwords

I had no intend to span all the aspects of bringing Backbone onto TypeScript ground. It would be impossible in boundaries of an article. Instead I wanted to show that is no rocket science switching to TypeScript, but advantages of such move cannot be overestimated. Here I give a few sources that may help you on the start.

Nowadays JavaScript evolves fast. We have ECMAScript 6th and 7th editions released over just 2 years. the current support of ES6 specification in major browsers is close to 100%. So the language changed, and changed for better. It has become more expressive, much more concise and readable. It is tempting to jump into, but what about older browsers? Transpilers can translate ES.Next into the old-good JavaScript of ECMAScript 5th edition.

Then the question is which transpiler to pick up? Babel (https://babeljs.io/) is still on the rise, but look what is happening to TypeScript. Since it was chosen for Angular2 it is gaining its momentum. Why have they decided for TypeScript? Because it's much more than transpiler. TypeScript enhances JavaScript with optional features such as static typing, interfaces, abstract classes, member visibility and more. That's a completely new level, not available in plain JavaScript.

Who's the dude?

Dmitry Sheiko is a web-developer living and working in Frankfurt am Main, DE