As you probably know, Typescript 1.5 was officially released couple of days ago (to be precise, it was on 18. 7. 2015 based on github release notes). When you combine it together with the facts that Microsoft open-sourced the language and partnered with Google to make Typescript main development language for upcoming Angular 2.0, you will end up with some crazy hype being hyped even more than we’re used to… (our lovely Javascript became pretty intense lately)

Are you Hyped yet ?! Good !

But let’s take it slow for now… The chances are pretty high that the project you’re currently working on professionally is being implemented using only vanilla Javascript or ES6 if you’re lucky. I know that you startup hipsters are laughing out loud now, but there is also this thing called corporate software development and risk aversion so bear with me!

The Basics

Typescript, while being superset of Javascript, still isn’t the Javascript itself. To use Typescript in your project you have to convert your whole code base. This could be a quite hard pill to swallow for your your managers, co-founders or even conservative part of your own personality.

But what if the initial code base conversion is achievable with a minimal downtime ?

The Context

I am currently working on what I would describe as a rather larger project (insurance industry) together with more than a sixty very skilled developers grouped into multiple cooperating teams.

As for the stack, it’s a Angular JS (1.4) portal with JavaEE back-end (nothing surprising here) but there is a twist. A lot of mission critical business logic is implemented in a separate node.js project. The kind of business logic which you want to have covered with literally thousands of unit tests… And all this madness just by using Vanilla (read ES5) Javascript!

Proof of Concept

The client is luckily open to innovation and decided to explore possibility of using Typescript in their code-base. This manifested into a mandate to create proof of concept Typescript application by converting part of the already implemented functionality from above mentioned node.js project.

Requirements & Goals

To effectively prove something, context must be clear and well defined. In our case we came up with the following set of requirements:

Use Typescript (obviously) Convert main module and one submodule from original node.js application Structure code and dependencies in a way that is usable by browserify and require-able by other node.js applications as a module Create simple build process with support for linting, compilation and testing of the source code and make it work well with compiler and test runner provided by the IDE (in our case Webstorm) Generate source maps so that is is possible to debug directly in Typescript Bonus: Maintain low coupling between submodules, and follow already defined dependency modeling guideline (I am currently writing another article on how to structure dependencies in node.js project)

The first point is obvious and the second point is fairly specific to our architecture. Notice that we have chosen just right amount of code for the conversion so that we can cover all the specified requirements and prove validity of our concept with minimal effort.

Make it browser & node.js friendly

Original project is collection of standard node.js modules implemented using require and exports syntax. That means it is automatically possible to use it in another node.js project and it works really well with browserify too.

Our Typescript solution contains .ts source files in which we are using new ES6 module syntax (import, export). These source files are then compiled using Typescript compiler with specified --module commonjs parameter during grunt build process or on the fly by IDE’s compiler. Result of the compilation consists of Javascript files in which ES6 syntax was replaced by familiar node.js module syntax.

It is again possible to run it in browser (with help of browserify) which traverses dependency tree and wraps and concatenates modules in correct order. Besides that application is still require-able by another node.js application, let’s say an express.js web application which exposes provided functionality as REST interface. This is achieved by specifying compiled app.js file as a main property in the package.json instead of the .ts file.

Building, testing & tooling

Grunt is still our go to solution for taking care of all build tasks. There are couple of Typescript grunt plugins available but we decided to go for the “official” one.

Grunt-ts is an npm package that handles TypeScript compilation work in GruntJS build scripts. It provides a Grunt-compatible wrapper for the tsc command-line compiler

Building the project should be easy in both CI and DEV environments.

In DEV environment we want to achieve developer experience that is as smooth as possible. Good thing about the Webstrom IDE is that it comes with embedded Typescript compiler (with possibility to override it and provide path to newer version). Webstorm compiles all changed .ts files on the fly and it nicely groups their compilation products (.js and .map.js) in the project structure browser. This neat feature prevents getting lost in visual clutter caused by tripled number of the files.

On CI server we run grunt task which lints and compiles all .ts files and then runs all the tests against the compilation products. Simple…

Testing

Tests are working great for both build and development modes out of the box. We just had to specify path to module/test folder (and check the Include sub directories option) which contains both .ts and compiled .js files. This enabled us to benefit from type checking also during the writing of the tests. Tests are then seamlessly run by mocha which in fact runs compiled version of the tests but you can still debug them in .ts.

As for the grunt, nothing surprising there. Just specify correct order of the build tasks and grunt mocha plugin will run the compiled tests as usual.

Linting

In our original project we are using combination of jshint and jscs grunt tasks and of course there is a similar solution available for the Typescript too. Enter Tslint with all the available rules. To facilitate smooth .js to .ts transition we disabled couple of rules like:

no-require-imports

no-var-requires

typedef

… which we are planing to enable again as soon as we added enough types :)

Debugging directly in Typescript

Source maps produced by Typescript compiler are OK by themselves but the problem occurred because of the two step nature of our build process.

Compile .ts files into .js files while generating .map.js files for every single one of them Generate concatenated .js bundle (and respective source map) by traversing dependency tree of compiled .js files with browserify

As you can see both steps of our build process generate source maps. Browserify unfortunately doesn’t check if there aren’t any source maps available on the input so the Typescript information gets lost.

We solved this problem by using sorcery.

Most compilers don’t check for intermediate source maps. (UglifyJS is an honourable exception.) So when you fire up devtools, instead of looking at the original source you find yourself looking at the final intermediate step in the chain of transformations. Sorcery aims to fix that.

Unfortunately there isn’t any grunt wrapper for sorcery which is a npm package so we executed it with help of grunt-exec. As mentioned in sorcery’s documentation, it is a work in progress library so we weren’t surprised when we encountered a a little problem with paths which was solved by copying the bundle and it’s source map into the root project folder (and then back into dist after everything was done).