Converting an existing Angular JS application to TypeScript
With the AngularJS team deciding to adopt a super-set of the TypeScript language, AtScript, TypeScript is clearly a language with a degree of momentum behind it. By adding type annotations and backporting ECMAScript 6 features TypeScript promises to make large JavaScript projects easier to maintain and understand. With that in mind our team decided to investigate adopting the language in our Connections simulator project, a relatively large and complex AngularJS application currently written in ECMAScript 5.
Whilst there are various guides out there to creating AngularJS projects with TypeScript from scratch, little has been written about porting existing applications, so what follows is a summary of our approach and some of our findings.
As with any serious refactoring effort, where possible an incremental approach is best. fortunately we have a comprehensive suite of unit and end to end tests so we can check we aren't breaking things as we gradually evolve the code from JavaScript to idiomatic TypeScript. With this approach we can split the changes into several phases:
First things first, we need an environment where we can compile, lint and test TypeScript alongside JavaScript. Whilst Gulp may be the new hotness for now we are using the Grunt build system as we have invested in it, including building some of our own plugins to support Connections development.
We used the obvious choice of grunt-ts to handle typescript compilation. Rather than use a globally installed version of the TypeScript compiler (tsc), we used npm to install tsc locally to the project, this way the TypeScript version is controlled within the project environment and builds will be reproducible.
For linting tslint proves comprehensive and an easy replacement for jshint, hooked into our build system via grunt-tslint. Testing remains handled by Jasmine/Karma with test tasks now involving a compilation step for both tests and testees before testing.
First we need to retrieve TypeScript definition files that will give us typed interfaces to the various components on which we depend, in our case Angular and angular-translate for the main application and Jasmine for the tests. These definition files can be manually downloaded however we used the TSD typescript definition manager which automatically downloads definitions specified in a configuration file. This allows us to avoid checking definition files into version control, in a similar way to which we use npm and bower to manage packages. Unfortunately for those coming from npm the TSD tool can be a little unintuitive, and it it unfortunate to have to introduce yet another package manager, but at this point there appears to be no superior option.
With definition files in place, all that remained was to rename all our .js files .ts, easily achieved with a bit of command line voodoo, and insert references to the required definition files. The syntax for these references is a bit odd but seems to be a legacy of TypeScript's integration with Visual Studio, for example:
///<reference path="../../../typings/angularjs/angular.d.ts"/>
With all this in place we were able to compile and run our test suite to make sure everything still worked as expected. This represents the minimum needed to port the entire application to TypeScript, however at this point we aren't actually leveraging any typescript features within our own code, to get real value from TypeScript we need to do a couple more things which will be detailed in part 2.
Keir Lawson
Tags: angular typescript
January 13th 2015