1 of 52

</>

2 of 52

How TypeScript

saved AngularJS app

3 of 52

Schedule

  • Is JavaScript so bad?
    • ...and what is wrong with Coffee?
    • The ultimate answer: TypeScript
  • Intro to TypeScript
    • types
    • interfaces
    • function literal
    • classes
    • generics
    • modules
  • AngularJS application conversion to TS process
    • **/*.{js => ts}
    • DefinitelyTyped
    • Entities
    • Type your services
    • $scope and controller with TS
  • To class or not to class
  • AngularJS DI vs. TS modules
  • Bonus

4 of 52

JavaScript

5 of 52

function (data, label) {

var output;

if (data.parts.indexOf(label) < 0) {

return http(label, data);

}

if (isString(data)) {

output = dataGetter(data, label);

} else if (data) {

output = data[label];

}

cache.put(label, output);

return output || data;

}

6 of 52

CoffeeScript

7 of 52

(data, label) ->

return if data.parts.indexOf label > 0 then http label, data

output =

if isString data then dataGetter data, label

else if data then data[label]

cache.put label, output

output or data

8 of 52

TypeScript

9 of 52

(data: Data | string, label: string): Promise<DataPart> => {

var output: DataPart;

if (data.parts.indexOf(label) < 0) {

return http(label, data);

}

output = typeof data === ‘string’ ?

dataGetter(data, label) : data[label];

cache.put(label, output);

return Q.when(output);

}

10 of 52

Typed JavaScript

11 of 52

Types

var nick: string;

var bottom: any;

var age = 31;

nick = ‘qba’;

nick += 84;

age += 1;

age = ‘old’;

bottom = 123;

bottom += ‘px’;

string

number

boolean

any

Array<T>

Funciton

Object

12 of 52

Interfaces

interface IPoint {

left: number;

top: number;

}

interface ICallback {

(...args: any[]): any;

}

interface IPath {

points: Array<IPoint>;

color: string | number[];

render(c?: ICallback): boolean;

add(p: IPoint): void;

}

13 of 52

Function literal

function (n) {

return n + n;

}

(n) => {

return n + n;

}

(n) => n + n;

14 of 52

Function literal

var _this = this;

function (n: number): number {

console.assert(_this === this); // fail

return n + n;

}

(n: number): number => {

console.assert(_this === this); // success

return n + n;

}

15 of 52

Classes

class Path implements IPath {

points: Array<IPoint> = [];

color: string | number[];

private position: IPoint;

constructor(color: string | number[]) {

this.color = color;

}

render(callback?: ICallback) { /* impl */ }

add(point: IPoint) { /* impl */ }

}

16 of 52

Classes (compiled to JS)

function Path(color) {

this.points = [];

this.position = null;

this.color = color;

}

Path.prototype.render = function (callback) { /* impl */ }

Path.prototype.add = function (point) { /* impl */ }

17 of 52

Generics

interface IWrapper<T> {

data: T;

$resolved: boolean;

$promise: IPromise<T>;

}

interface ICollection<T> extends IWrapper<Array<T>> {

hasNext(): boolean;

next(): T;

}

18 of 52

Modules (declarations)

declare module demo.path {

interface IPoint { /* decl */ }

interface ICallback { /* decl */ }

interface IPath { /* decl */ }

}

19 of 52

Modules (implementations)

module demo {

export var version = ‘0.0.1’;

}

module demo.path {

export class Path implements IPath { /* impl */ }

}

alert(demo.version);

var xPath: demo.path.IPath = new demo.path.Path(‘#da1’);

20 of 52

AtScript

-annotations

-introspection

TypeScript

-types

-enums

ES6

-classes

-modules

ES5

21 of 52

Angular { JS => TS }

22 of 52

Rename

23 of 52

Compile

Fix

Lint

errors?

yes

no

24 of 52

DefinitelyTyped

angular-1.2.d.ts

// Type definitions for Angular JS 1.2+

// Project: http://angularjs.org

// Definitions by: Diego Vilar <http://github.com/diegovilar>

// Definitions: https://github.com/borisyankov/DefinitelyTyped

25 of 52

DefinitelyTyped

angular.module(‘myApp’).service(‘myService’,[

‘$http’, ‘$q’, ‘myCache’,

function ($http, $q, myCache) {

/* impl */

}

]);

26 of 52

DefinitelyTyped

angular.module(‘myApp’).service(‘myService’,[

‘$http’, ‘$q’, ‘myCache’,

function (

$http: ng.IHttpService,

$q: ng.IQService,

myCache

) { /* impl */ }

]);

27 of 52

Compile

Fix

Lint

errors?

yes

no

28 of 52

Extend DefinitelyTyped

  1. Context value holders:
    • $rootScope
    • $routeParams
    • currentRoute
    • $scope
    • $attrs
    • controllers
  2. Decorated services

29 of 52

Extend DefinitelyTyped

declare module ng {

interface IRootScope {

user: IMyAppUser;

}

}

declare module app.directives {

interface IProgressBarAttrs extends ng.IAttributes {

myProgressBar: string;

myProgressLoader?: string;

}

}

30 of 52

Compile

Fix

Lint

errors?

yes

no

31 of 52

Used Entities

declare module app {

interface IQueryParams {

limit: number;

offset: number;

filter?: string;

search?: string;

order?: string;

desc?: boolean;

}

}

32 of 52

Compile

Fix

Lint

errors?

yes

no

33 of 52

Services

angular.module(‘myApp’).service(‘myService’,[

‘$http’, ‘$q’, ‘myCache’,

function (

$http: ng.IHttpService,

$q: ng.IQService,

myCache

) { /* impl */ }

]);

34 of 52

Services

declare module app.common {

interface IMyCacheService {

get(key: string): any;

put(key: string, data: any): any;

remove(key: string): void;

}

}

35 of 52

Services

angular.module(‘myApp’).service(‘myService’,[

‘$http’, ‘$q’, ‘myCache’,

function (

$http: ng.IHttpService,

$q: ng.IQService,

myCache: app.common.IMyCacheService

) { /* impl */ }

]);

36 of 52

Compile

Fix

Lint

errors?

yes

no

37 of 52

Controllers, Scopes [, Attrs]

app.controller(‘MyCtrl’, function ($scope: ng.IScope, data) {

$scope.data = data;

});

38 of 52

Controllers, Scopes [, Attrs]

interface IMyCtrl { /* declaration */ }

interface IMyScope extends ng.IScope {

data: any;

}

app.controller(‘MyCtrl’, function ($scope: IMyScope, data) {

$scope.data = data;

});

39 of 52

Controllers, Scopes [, Attrs]

interface IMyScope extends ng.IScope {

ctrl: IMyCtrl;

data: any;

}

controller: ‘MyCtrl as ctrl’,

40 of 52

Controllers, Scopes [, Attrs]

link: function (

scope: ng.IScope,

element: ng.IAugmentedJQuery,

attrs: ng.IAttributes,

ctrl

) { /* impl */ }

41 of 52

Controllers, Scopes [, Attrs]

link: function (

scope: IMyScope,

element: ng.IAugmentedJQuery,

attrs: ng.IAttributes,

ctrl: IMyCtrl

) { /* impl */ }

42 of 52

Controllers, Scopes [, Attrs]

interface IMyDirectiveAttrs extends ng.IAttributes {

myDirective: string;

myDirectiveConfig?: string;

}

attrs: IMyDirectiveAttrs,

43 of 52

Compile

Fix

Lint

errors?

yes

no

44 of 52

WTF ?!? CLASS in JS ?!?

45 of 52

KEEP

CALM

and

I M P L E M E N T

YOUR DECLARATIONS

46 of 52

Class vs. Angular

module app {

class MyCacheService implements IMyCacheService {

// implementation

}

angular.module(‘app’).service(‘myCache’, MyCacheService);

}

47 of 52

Class: dependency injection

class MyCacheService implements IMyCacheService {

constructor($parse, $cookies) { /* impl */ }

get(key: string) {

$parse( … )

};

/* impl */

}

// WILL NOT WORK !!!

48 of 52

Class: dependency injection

class MyCacheService implements IMyCacheService {

constructor($parse, $cookies) {

this.get = function(key: string) { /* impl */ };

this.put = function(key: string, data) { /* impl */ };

this.remove = function(key: string) { /* impl */ };

}

}

// !! UGLY

49 of 52

Class: dependency injection

class MyCacheService implements IMyCacheService {

static $inject = [‘$parse’, ‘$cookies’];

constructor(

private $parse: ng.IParseService,

private $cookies: ng.cookies.ICookiesService

) {}

/* impl */

}

app.service(‘myCache’, MyCacheService);

50 of 52

Benefits

  • another testing step
  • self documenting code
  • better maintainability
  • easier reuse (modularity++)
  • less frustration

51 of 52

THX

52 of 52

Jakub . Strojewski @gmail.com