Spiria logo.

AngularJS + TypeScript

September 26, 2016.
Angular + TypeScript.

TypeScript has been around for a while but I refrained from looking into it for the longest time because, like you, no doubt, I believed that any language whose mission is to produce another language could only be a disaster. Recently though, we at Spiria embarked upon a project in which we wished to use AngularJS 1 as a front-end language. Since many of our team members are .NET people, we wondered whether it would be possible to build an Angular application in TypeScript. I was tasked with looking into it, and I am pleased to present the results of several days’ research (spoiler alert: the results are positive).

JavaScript people, rejoice: your TypeScript project will work seamlessly with your favorite test and automation tools (in my case, Gulp and Karma/Jasmine), and you can even automate your TypeScript compilation in JavaScript as you save your files!

TypeScript

Basic resources

These resources, found on TypeScript’s official Website, will give you a good idea of what TypeScript can do. I won’t repeat what is perfectly well covered on the site; I recommend you go have a look and play around with TypeScript. Throughout this article, I’ll refer to aspects of TypeScript that are covered in the Handbook.

TypeScript equals JavaScript

The most important thing to remember is this: TypeScript is in effect JavaScript. Tools that are compatible with JavaScript files (Gulp, Grunt, Jasmine, etc.) will not go to waste and will be 100% useful without changing a thing to your testing or deployment process, aside from adding a compilation step to TypeScript in JavaScript at the very beginning of your process.

The second-most important thing to remember is: valid JavaScript code is valid TypeScript code. Consider TypeScript as a “superset” of JavaScript with extended syntax to better manage object-oriented concepts and code modularisation.

Finally, the third-most important rule: TypeScript syntax errors do not necessarily make JavaScript syntax errors. At the end of the line, TypeScript compiles in JavaScript, and though the TypeScript function expects a string, it can still handle an integer, as JavaScript simply performs its peerless automatic conversion:

function newAlert(msg: string) {
    alert(msg);
}
newAlert(4); //TypeScript Error, but since JavaScript performs its famous automatic conversion, it will put out alert "4" after compilation.

In other words, it makes cheating possible. But since the whole point of TypeScript is to stop cheating and to ensure that objects are used properly and contracts are respected, let’s avoid cheating. I mention this at the outset to make it abundantly clear that TypeScript will not code for you; it merely helps you better structure your code.

Now, let’s see how this works with Angular.

AngularJS

We’re not talking about Angular 2, but rather the classic version, AngularJS 1. Why? Because Angular classic has stood the test of time, and though Angular 2 was built with TypeScript in mind, our comparison will be more effective using a well-known system.

Basic resources

Developers versed in Angular and/or NodeJS and/or ECMA2015 will no doubt be familiar with what they’ll find in the ToDoMVC application code. However, if you’re new to this, you’re not expected to wander about the code, figuring it out yourself. Instead, let me escort you through it all and explain what TypeScript is doing in the background to generate a working Angular application in JavaScript.

First, go to the Application.ts - not to be confused with the Application.js file - which is a concatenation of all the compiled TypeScript files. This file is the result of a tsc command with the -out parameter. You can always go to this file to see how TypeScript compiles in JavaScript, but we’ll come back to this later; for the time being, let’s concentrate on Application.ts.

You will see the following code:

/// 
module todos {
    'use strict';
 
    var todomvc = angular.module('todomvc', [])
            .controller('todoCtrl', TodoCtrl)
            .directive('todoBlur', todoBlur)
            .directive('todoFocus', todoFocus)
            .directive('todoEscape', todoEscape)
            .service('todoStorage', TodoStorage);
}

First, the following comment,

///

is simply an instruction to the TypeScript compiler to include code from another file, i.e. _all.ts. If you go to this file, you’ll see that the author has included other files, including .d.ts libraries and the .ts files of their own creation *.

(*Personally, I’m not a big fan of the TypeScript file reference method, since, manually speaking, it’s one more thing to think of. But never fear, there are other, automated alternatives to spare us that headache.)

Speaking of .d.ts files:

///

As you might guess, this is a reference to the Angular library in TypeScript mode. A quick look at the file shows nothing but interface declarations covering all of Angular’s public functions. These interfaces make it possible to use Angular functions in a TypeScript environment, reinforcing object types and parameters without re-keying the entire library. Technically, the .d.ts file is optional, but you do run the risk that your TypeScript compiler will send you non-declared object error messages and you’ll have to constantly check that you’re using these methods correctly. In other words, you’ll be using Angular as if you were working in pure JavaScript. But since the Internet is a magical place, you’ll find hundreds of popular JavaScript libraries with .d.ts equivalents right here: DefinitelyTyped.

Now,

'use strict';

is to instruct the javascript compiler to run in 'strict mode'. Amongst other things, it throw errors when you're using variables that have never been declared. Trust me, it helps. More information here.

Next, the modules:

module todos { ... }

Simply put, a module is a namespace. JavaScript being what it is, a module is also an object and in traditional JavaScript, “modules” are declared as follows:

var todos;
(function (todos) {
    ...
})(todos || (todos = {}));

If this bit of code is giving you a headache, remember that this is exactly the problem that TypeScript is trying solve. For further information on the JavaScript module pattern, visit this link.

Briefly put, in this module, you define functions, interfaces, classes and properties (since our module is also an object) that can be made public by placing the export term before the declaration. You can declare one single module in several different places and even files. As long as the other files are referenced as described above, everything that has been declared in this module is shared throughout every declaration.

module Foo {
    export function bar() {
        alert('Hello World!');
    }
}
module Foo {
    bar(); //alerte 'Hello World!'
}

Without going into too much detail, the neatest thing about modules is that you can import one module into another module (or anywhere else, for that matter):

module Foo {
    export function bar() {
        alert("Hello World!");
    }
}
module Main {
    /// 
    import foo = Foo; //Optional, but it avoids later refactoring should you decide to change the name of module Foo. It’s just a good habit to get into.
    foo.bar();
}

Finally, the code

The rest of the code, if you are at all familiar with Angular (which you are, since you took the Angular tutorial before reading this), is practically pure JavaScript. You declare the Angular module to which you’ll attach controllers, directions and services, exactly like in JavaScript.

The TodoCtrltodoBlurtodoFocustodoEscape and TodoStorage functions are declared in the other referenced files, and, since they’ve all been declared in the same todos module, they are available by default. But you’re in for a surprise: if you go see these functions, you’ll find that they are no longer functions but rather classes!

A classy act

In TypeScript, you can use classes to replace JavaScript’s “functions-classes”. Those of you who are very familiar with C# or Java-type languages will know what I’m talking about. Without entering into the finer points of declaring and using classes in JavaScript, here is roughly how it goes:

function Person() {
    //Non inherited methods here
}
 
Person.prototype = {
    //Inherited methods here
    setName: function (name) {
        this.name = name;
    }
}
 
function TalkingPerson() {
}

//__proto__ est is basically a definition of the structure of the object. Any changes made to __proto__ is reflected in all objects instantiated through 'new TalkingPerson()'
//here, you’re telling the JavaScript engine that all Person prototype methods are also accessible to the TalkingPerson prototype
TalkingPerson.prototype.__proto__ = Person.prototype;
TalkingPerson.prototype.sayName = function () {
    alert('My name is ' + this.name);
}
 
var jane = new TalkingPerson();
jane.setName('Jane');
jane.sayName(); //alert 'My name is Jane'
 
var john = new Person();
john.setName("John");
john.sayName(); //'sayName' method undefined

It looks simple enough, but it quickly gets tricky. Just managing the scope (the infamous this variable) quickly becomes a nightmare when you play around with object prototypes. I challenge you to play around with this bit of code: add prototype properties and methods, and have a look at what is accessible to referenced objects, what is shared with other objects, etc., and you’ll come to fully appreciate TypeScript.

For comparison purposes, here is the same piece of code in TypeScript:

class Person {
    name: string;
    setName(name) {
        this.name = name;
    }
}
class TalkingPerson extends Person {
    sayName() {
        alert(this.name);
    }
}
var jane = new TalkingPerson();
jane.setName("Jane");
jane.sayName();
var john = new Person();
john.setName("John");
john.sayName();

So simpler and much more intuitive!

Getting back to Angular

Traditionally, we’d have defined the object, ToDoCtrl through a function and assigned it the property $inject for Angular to inject a dependency. Then, we would have defined the properties and methods available for our controller, as well as some private functions, as follows:

TodoCtrl.$inject = ['dependency1', 'dependency2'];
function TodoCtrl(dependency1, dependency2) {
    // 'vm' means 'view model'. This style is used to access Angular’s 'controller as' function. See JohnPapa’s style guide, above.
    var vm= this;
 
    vm.foo = 1;
    vm.one = dependency1.getOne();
    vm.two = dependency2.getTwo();
    vm.bar = bar;
  
    function bar(){
        vm.foo++;
    }
}

This is what it would have looked like in TypeScript:

class TodoCtrl {
 
    public foo: integer;
    public one: IOne; //You define the interface by default, unless it’s an external library extracted from the .d.ts file
    public two: ITwo;
  
    public static $inject = ['dependency1', 'dependency2']; //Public for Angular to see it, and static for the injector to do its work before instantiating the class.
  
    constructor(private dependency1: IDependency1;, private dependency2: IDependency2){
        this.foo = 1;
        this.one = dependency1.getOne();
        this.two = dependency2.getTwo();
    }
  
    bar(){
        this.foo++;
    }
}

Perhaps more wordy, but so much more readable. The simple fact of declaring a property or a method as public clearly means that we’ll be able to perform our binding with it, while the private ones are also clearly indicated and therefore not externally accessible.

Therefore

In the Application.ts file, controllers, directions and services linked to our Angular application (lines 12 to 16) are class definitions. When generating the application, Angular uses these class definitions to instantiate the instances it will require to bring your application to life. Now explore the Application.js file; by now, you should be able to understand what is going on by comparing it to the .ts files of the controllers, services and directives.

And finally

I recommend you follow the instructions in the ReadMe file of the excellent TodoMVC demo to learn how to deploy the project on your machine and compile TypeScript via a npm command. For those of you who are not at all familiar with Node.js and npm, here is a summary of how it works:

  • NodeJS is a scripting language akin to PHP, Python or Ruby, that you install on your machine (the “server”) to execute scripts. As its name indicates, NodeJS is pure JavaScript.
  • npm is the Node Package Manager, similar to gem for Ruby, pip for Python, composer for PHP or even nuget for .NET.
  • When instantiating a npm project with package.json, you create a package.json file containing the list of all dependencies to be installed for the application to work.
  • The package.json file, which can be seen in the TodoMVC application, can also contain scripts, or commands that can be executed by calling npm name-of-script (in TodoMVC, npm compile will execute the command tsc --sourcemap --out js/Application.js js/_all.ts.
  • tsc is the command that compiles TypeScript in JavaScript. --sourcemap enables sourcemapping. --out js/Application.js is the instruction to link the results of the compilation in a single file. Finally, the last parameter, js/_all.ts, indicates which file to compile. In this case, the file is empty, but since it references other files, the compiler will also seek those out to compile them.
  • index.html contains the reference to the compiled file, Application.js, as well as the required JavaScript libraries, including Angular. Note that the libraries are still in JavaScript; simply including a .d.ts in your TypeScript does not necessarily mean that the JavaScript library will be included in your code.

Next installment

In a future installment, I’ll explain how I explored ways to automate my application build (i.e. compiling my TypeScript in JavaScript) with Gulp. I’ll also explain how I explored Jasmine for unit tests, and Karma for continuous execution of these tests. All these dependencies are managed with npm, a go-to resource for the JavaScript community.