As I mentioned in my previous story, I am a TypeScript convert these days. I enjoy everything it has to offer, classes, namespaces (modules), easy to read inheritance, generics, static typing, compile time errors, and probably many more things that I haven’t quite yet discovered.

However, I work exclusively with Angular now, and I have been trying to figure out if TypeScript is a good option or not. I am currently in the middle of a research run to try to see if upgrading our JavaScript code to TypeScript and make it 100% compatible is possible at all or not. And probably more importantly, if the time that is going to take other developers to catch up and learn the language is worth it or not.

Without much delay, I am going to start converting some easy Angular concepts into their TypeScript counterparts.

Controllers

Controllers in angular are plain functions with methods and properties. If you already use controllerAs syntax then you don’t have to worry where the scope is. It actually becomes the object context itself. Let me show you what I mean.

This is a JavaScript Angular Controller that uses the controllerAs syntax:

angularApp.controller('MyController', ['$window', function($window) {
    // We injected the $window service into the controller
    // There is no need to use $scope in here, instead use -this-

    this.thisIsTheViewModel = true;

    this.getAllSectionTags = function() {
        return this.$window.document.getElementsByTagName('section');
    }

}]);

Ok in this example controller we inject the $window service, and we have a public property and methods available. The fact that we use controllerAs syntax allows us to instantly make those properties and methods available in the scope for the views or directives to use when they want. So yes that means that thisIsTheViewModel and getAllSectionTags() are available to be bound into the views already.

Now, how do we convert this into a TypeScript class? Well if you spend some time checking out the way TypeScript converts the code into JavaScript functions you will realize that a class is pretty much converted into the kind of function that we need.

The controller as a TypeScript class:

class MyController {
    var thisIsTheViewModel: boolean = true;

    constructor() {
    }

    getAllSectionTags(): Array<any> {
        return this.$window.document.getElementsbyTagName("section");
    }
}

// And now use it in Angular:
angularApp.controller(MyController);

Cool right?

Ok, first issue: I’ve ignored the return type of the getAllSectionTags() method and use an Array of type any. For now that works, but it is a bad practice, and I am a bad person for suggesting it. This is TypeScript, you should always use the right types, what is the point if you don’t?

Second issue: if you run this code in the browser and bind the method to a click event somewhere, you will realize that you get an error complaining that $window doesn’t exist in the this object.

This happens because we have lost the lexical scope when we bind the method to a button in the UI. But no worries there is another great TypeScript feature that comes to our rescue for this, the fat arrow. Let’s rewrite your method like this and it will work as expected:

var getAllSectionTags = (): Array<any> => {
     return this.$window.document.getElementsbyTagName("section");
} 

Third issue: We need to inject the $window service somehow. Where do we do that you might ask? Well, directly into the constructor my friend. However we have two options:

First option: The dependencies array

This way we will inject the dependencies into the angular.controller() method itself like this:

angularApp.controller(["$window", MyController]);

And modify the constructor like this:

constructor(private $window: angular.IWindowService) {
}

Passing the $window service with the private keyword automatically converts it into a private property inside the class. This is very helpful because we make it available to the rest of the methods in the class, but we don’t really expose it outside it (it will however be compiled into a property in the JavaScript prototype that will be visible in the developer tools of your choice though, keep that in mind).

Ok this works, but I don’t really like the syntax, you will have to change the array in both places and you need to keep the angular.controller() line all the way to the bottom of your class or you will get annoying warnings about the class not being defined. So imagine when you have a decent amount of code in your class you will likely forget to modify the array at the bottom of the file. This might sound stupid and first-world-problem kind of whining, but I guarantee you that it will get annoying very fast.

Second option: static inject

This is a very cool feature in Angular that might not look very helpful when you work in JavaScript, but it is incredibly handy in TypeScript. You can create the array of dependencies by telling the injector which ones you need, let’s write it down:

class MyController {
    ...
    static $inject = ["$window"];
    constructor(private $window: angular.IWindowService) {
    }
    ...
}

angularApp.controller(MyController);

That’s it, you don’t need anything else. Now your dependencies array is minification safe, and also lives right next to your constructor. Which is very helpful when you need to constantly change the dependencies you are injecting, which actually happens a lot more than you think.

All right, that is all for controllers. Next.

Services and Factories

Angular Services and Factories are very similar in Angular in JavaScript, however they are different in TypeScript. The reason being a service will be instantiated by Angular, but a factory on the other hand, needs to return a new instance of itself when used by Angular.

That might sound confusing, understandable. This took me a while to figure out, I can easily convert a service into a class, because Angular will create the singleton by doing something like new MyService() and it will pass all the dependencies you have indicated.

However a factory is not instantiated in the same way, Angular expects you to return a new instance of the class, so you are going to have to create a static method in the factory class that will return the instance that will be used as the singleton by Angular.

Whaaaa, that was way too confusing.

My quick recommendation, don’t use factories for now. Start by adding all your services and factories as services instead. You can worry about factories being factories a little down the road. You might change your mind about it when you see how easy is to create services.

The service class

Well, it actually is exactly like the controller, no kidding:

class MyService {
    static $inject = ["SomeOtherService"];
    constructor(private someOtherService: ISomeOtherService) {
        // the dep injected as an interface instead .. welcome testing
    }
}

angularApp.service(MyService);

Easy enough. Let’s move on to factories

The factory class

As I mentioned before factories cannot be instantiated by Angular if we create them as plain classes, however you can expose a static method to return a new instance of the factory, and use that static method in your angular definition, like this:

class MyFactory {

    constructor() {
    }

    static Factory(): MyFactory {
        return new MyFactory();
    }

}

angular.factory(MyFactory.Factory());

Ok so this works, however you might be already wondering how to inject dependencies into this factory.. Well my short answer is I don’t know yet. If I try to add them to the constructor then the factory function will complain asking for the constructor parameters, which I cannot pass because Angular is supposed to do that for me.

You could use the $injector service to do it manually inside the constructor, but I hate the idea, I think it makes the class looks like spaghetti. But that is my opinion.

I will definitely modify this story as soon as I figure out a better way to do this, in the meantime, I am not using factories and I am very happy about it.

That is a long story, it is late at night, and I am getting tired. I will continue with Directives in a story soon.

Thanks for reading.