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,141