Type safe Angular with TypeScript

Nick Tsitlakidis

Software Developer @ AMD Telecom, Routee team

Agenda

  • What is TypeScript
  • Ok, but I work on Java APIs
  • Existing libraries
  • Mixing with Angular
  • Tooling

Yet another *Script?

JavaScript with types and a helpful compiler

What is it?

  • Open source with all development happening on Github.
  • Introduced by Microsoft but more are actively contributing.
  • JavaScript superset
  • Valid JavaScript is valid TypeScript too.
  • Focusing on ES features to be available in the future.
  • Adds classes, interfaces, (proper) inheritance etc, but...
  • They are optional!

Basic Types

  • boolean
  • number
  • string
  • array
  • tuple
  • enum
  • any
  • void

Syntax



var counter:number = 0;

var counterStarted:boolean = (counter != 0);

var counters:Array<string> = [];

counters.push(counter.toString());

counter will be treated as numeric

Superset of JavaScript

The good!

  • You can migrate your code at your own pace
  • Compiler output is readable (and debuggable)
  • Most of the features will be available in future versions of JavaScript.
  • No special runtime required
  • Compiles to ES3 / ES5 / ES6 (ES2015)

Superset of JavaScript

The complicated!

  • Your build process needs new steps (compilation)
  • You need to decide about module systems 
  • You need to learn about source maps if you want to debug easily
  • Some OOP concepts are not what you're used to

Superset of JavaScript

The code

function addTwoNumbers(one,two) {
    return one+two;
}

var result = addTwoNumbers(3,6); //this will work

var anotherResult = addTwoNumbers("3",6); //returns "36"
function addTwoNumbers(one:number,two:number):number {
    return one+two;
}

var result:number = addTwoNumbers(3,6); //this will work

var anotherResult:number = addTwoNumbers("3",6); //your IDE shows an error at compile time

OOP features

The good!

  • ES2015 Classes
  • Interfaces
  • Abstract classes
  • Polymorphism
  • Generics
  • Access modifiers
  • Decorators
  • No method overloading
  • No constructor overloading
  • Interfaces are gone at runtime
  • Types are gone at runtime

The complicated!

OOP features

The code!

interface CoffeeConsumer {
    drinkCoffee();
}

class Developer implements CoffeeConsumer {

    private name:string;

    constructor(name:string) {
        this.name = name;
    }

    public drinkCoffee() {
        this.takeABreak();
        this.prepareCoffee();
    }

    private takeABreak() {
        console.log("hurray, a break");
    }

    private prepareCoffee() {
        console.log("where is the sugar?");
    }
}

var exhaustedDev:CoffeeConsumer = new Developer("Dev");
exhaustedDev.drinkCoffee();
var Developer = (function () {
    function Developer(name) {
        this.name = name;
    }
    Developer.prototype.drinkCoffee = function () {
        this.takeABreak();
        this.prepareCoffee();
    };
    Developer.prototype.takeABreak = function () {
        console.log("hurray, a break");
    };
    Developer.prototype.prepareCoffee = function () {
        console.log("where is the sugar?");
    };
    return Developer;
}());
var exhaustedDev = new Developer("Dev");
exhaustedDev.drinkCoffee();

Different interface usages


interface Person {
    name:string;
    age:number;
}

var p:Person = {
    age:30,
    name:"Nick"
};

interface Person {
    name:string;
    age:number;
}

class User implements Person {
    public name:string;
    public age:number;
}

var p:Person = new User();
p.age = 30;
p.name = "Nick";

function addPerson(person:{ name:string; age:number; }) {
    console.log(person.age);
}

Direct object creation

Inline interface usage

Implemented by a class

Why it matters for APIs

@Getter
@Setter
public class ContactDto {
    private String id;

    private String firstName;

    private String lastName;
}

Your API response

$.getJSON("/contacts/345", function(data) {
    //do something with the response data
});

Your AJAX call

interface ContactDto {
    id:string;
    firstName:string;
    lastName:string;
}

$.getJSON( "/contacts/345", (data:ContactDto) => {
    console.log(data.firstName);
    console.log(data.lastName);
    console.log(data.id);
});

The problem with JS libraries

  • They are countless so we need to take advantage of them
  • We can't rewrite them
  • Writing adapters is also hard depending on the language
  • Ambient declarations to the rescue!
  • Declare syntax

Ambient Declarations

interface LoDashStatic {

 forEach<T>(
      collection: T[],
      iteratee?: ListIterator<T, any>,
      thisArg?: any
    ): T[];

 groupBy<T, TKey>(
      collection: List<T>,
      iteratee?: ListIterator<T, TKey>,
      thisArg?: any
    ): Dictionary<T[]>;

...

}

declare var _:LoDashStatic

interface Angular {
    controller(name:string, controllerClass:Function);
}

declare var angular: Angular;

angular.controller("MyController", MyControllerClass);

Typings

npm install -g typings
  • Typings manager available through npm
  • Typings for both the browser and node.js
  • Store the typings you need in a definitions file
  • Included through reference paths or tsconfig.json
typings install lodash --save
///<reference path="typings/browser.d.ts"/>

_.forEach([1,2,3],(value)=> {
    console.log(value);
});

No compiler errors + code hinting

Type safe Angular.js

  • Services can be classes
  • Controllers can be classes (using "controller as" syntax)
  • Lambdas can be used to avoid issues with "this"
  • Decorators can be used for common class tasks (injections)
  • Your models can now be described as interfaces or classes

Angular controller classes

interface User {
    firstName;
}

class UserProfileController {

    public user:User;
    private httpService:ng.IHttpService;

    constructor($http:ng.IHttpService) {
        this.httpService = $http;

        this.httpService({method: 'GET', url: 'http://my-api.com/users/356'})
            .then((user:User)=> {
                this.user = user;
            });

    }
}

angular.module("Profile").controller("UserProfileController",UserProfileController);

No more $scope! (most of the times...)

<div ng-controller="UserProfileController as controller">
    <p>{{controller.user.firstName}}</p>
</div>

Angular service classes

class UserService {

    private httpService:ng.IHttpService;

    constructor($http: ng.IHttpService) {
        this.httpService = $http;
    }

    public getUser(userId:string):ng.IPromise<User> {
        return this.httpService({
            method: 'GET',
            url: 'http://my-api.com/users/'+userId
        });
    }
}

angular.module("Services").service("UserService",UserService);

Angular services with Restangular

class UserService {

    private restangular:restangular.IService;

    constructor(Restangular:restangular.IService) {
        this.restangular = Restangular;
    }

    public getUser(userId:string):ng.IPromise<User> {
        return this.restangular.one('users', userId).get();
    }

    public updateUser(user:User):ng.IPromise<User> {
        return this.restangular.one("users", user.id).customPUT(user);
    }
}
  • Centralized configuration for all your api calls
  • Interceptors for responses and requests
  • Centralized authorization flow
  • Centralized error handling
  • Methods are based on RESTful concepts

Tools

  • Editors and IDEs
    • Jetbrains (Intellij IDEA / Webstorm/ PHPstorm)
    • Microsoft (Visual Studio / Visual Studio Code)
    • Atom
    • Sublime Text
    • NetBeans
    • Eclipse
  • TypeScript Playground
  • Webpack
  • Browserify
  • TypeDoc
  • Grunt / Gulp

Type safe Angular using TypeScript

By Nick Tsitlakidis

Type safe Angular using TypeScript

A little bit of Angular.js, a lot about TypeScript, a hint at what we do and a look into the future.

  • 2,104