Converting an existing Angular JS application to TypeScript
In my last post we took an overview of the steps taken to convert the Connections simulator to TypeScript. This post details the steps needed to refine our application to leverage the features TypeScript gives us, so following on from phase 2:
To make our code more idiomatic TypeScript we first need to take advantage of various ES6 features which TypeScript provides, primarily modules and classes.
JavaScript, of course, already has support for object orientation via prototypical inheritance, however, the class syntax introduced in ES6 and supported by TypeScript improves usability. TypeScript also add module support which though not aligning completely with the ES6 module syntax, is very close.
What this means for our application is converting our controllers and services away from the traditional angular syntax to make use of these new language features. For example a simple service such as:
'use strict';
angular.module('ccSimulatorApp')
.factory('safeApply', function ($rootScope, $timeout) {
var sa = {};
sa.apply = function() {
...
};
return sa;
});
Becomes
module Application.Services {
export class SafeApply {
static $inject = ['$rootScope', '$timeout'];
constructor (private $rootScope, private $timeout) {
}
apply() {
...
}
}
}
angular.module('ccSimulatorApp')
.service('safeApply', Application.Services.SafeApply);
Whilst the change in syntax isn’t any more concise it does allow us to use type checking when utilising this provider elsewhere. “private” constructor parameters are a shorthand syntax to allow us to set the injected services as private instance variables for access by methods.
There is one major gotcha at this step; by moving away from factory methods in favour of proper classes suddenly we need to use the “this” keyword to access state. As has been well documented JavaScript’s “this” is a tricky beast to master, especially when methods are being passed around as functions. ES5’s bind() allows us to mitigate these issues however, more information on issues with this can be found here.
Finally we’re now in a position to really leverage the advantage of TypeScript: type checking. We do this by peppering our function signatures with type annotations, with annotations covering the core angular functionality provided via the definition files installed earlier. For example:
constructor (private $rootScope: ng.IRootScopeService, private $timeout: ng.ITimeoutService) {
...
}
For any custom objects used we need to create object interfaces so that we can guarantee the objects comply with the expected schema, for example:
interface MainConfig {
editConfig: any;
onMainContentLoaded: any;
isConfigLoaded: boolean;
}
Adding type annotations also now means we can strip out any manual type checking code previously in place as this will now be handled for us.
TypeScript isn’t just type annotations, it also backports other ES6 features that we can take advantage of in our code. Arrow functions make for more concise code with more predictable “this” values, and recently released TypeScript 1.4 also adds support for block scoped variables and string interpolation (known as template strings). All of these we can make ample use of within our codebase.
Converting to TypeScript is not a totally seamless process, however much of the pain involved will be necessary when moving to ES6/Angular 2.0 anyway. The tools for TypeScript are fairly mature, though the definition retrieval tool could use a more intuitive user interface. This critical question however is; is it worth it? The productivity gains from a stronger type system are obviously difficult to gauge, but where large teams and large projects are concerned, these should be significant. Ultimately the investment in porting to TypeScript is a decision that has to be made on a project by project basis, not quite the definitive conclusion we might like but hopefully the above walkthrough of the porting process can give some insight as to whether it might be worth it for your project.
Keir Lawson
Tags: angular typescript
January 27th 2015