How we migrated our huge AngularJS app from CoffeeScript to TypeScript

32
HOW WE MIGRATED OUR HUGE ANGULAR.JS APP FROM COFFEESCRIPT TO TYPESCRIPT Luka Zakraj š ek CTO @ Koofr @bancek Ljubljana Spring-ish JavaScript Meetup February 10, 2015

Transcript of How we migrated our huge AngularJS app from CoffeeScript to TypeScript

Page 1: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

HOW WE MIGRATED OUR HUGE ANGULAR.JSAPP FROM COFFEESCRIPT TO TYPESCRIPT

Luka Zakrajšek

CTO @ Koofr

@bancek

Ljubljana Spring-ish JavaScript Meetup

February 10, 2015

Page 2: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

ABOUT MEFRI graduateCTO and cofounder at Koofr

frontend, backend, iOS, Windows Phonealmost 3 years of Angular.js

Page 3: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

KOOFRKoofr is white-label cloud storage solution for ISPs

Page 4: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

ANGULAR.JS AT KOOFRweb app

desktop application(web app wrapped into browser to look like native)

Page 5: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

HUGE APP?26 route controllers90 directives22 factories14 filters15 services

6012 LOC of Coffee (30 files)

2937 LOC of Angular HTML templates (101 files)

Page 6: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

WE WERE HAPPY WITH COFFEESCRIPTPros

clean codeclasses

Cons

technical debtfear of refactoringnot enough tests

Page 7: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

COFFEESCRIPTLaunched in 2010.

Gained traction with Rails support in 2011

$(function() { // Initialization code goes here})

$ -> # Initialization code goes here

Page 8: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

BROWSERIFYLets you require('modules') in the browser by bundling up all of your

dependencies.

var unique = require('uniq');var data = [1, 2, 2, 3, 4, 5, 5, 5, 6];console.log(unique(data));

$ npm install uniq$ browserify main.js -o bundle.js

<script src="bundle.js"></script>

Page 9: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

TYPESCRIPTa typed superset of JavaScript that compiles to plainJavaScript

any existing JavaScript program is also valid TypeScriptprogram

developed by Microsoft

from lead architect of C# and creator of Delphi and TurboPascal

Page 10: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

FIND A TYPO

class Point { x: number; y: number; constructor(x: number, y: number) { this.x = x; this.y = y; } getDist() { return Math.sqrt(this.x * this.x + this.y * this.y); }}

var p = new Point(3,4);var dist = p.getDst();alert("Hypotenuse is: " + dist);

Page 11: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

FEATURES

Page 12: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

JAVASCRIPT

function Greeter(greeting) { this.greeting = greeting;}Greeter.prototype.greet = function() { return "Hello, " + this.greeting;}

// Oops, we're passing an object when we want a string.var greeter = new Greeter({message: "world"});

alert(greeter.greet());

Page 13: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

TYPES

function Greeter(greeting: string) { this.greeting = greeting;}

Greeter.prototype.greet = function() { return "Hello, " + this.greeting;}

var greeter = new Greeter("world");

alert(greeter.greet());

Page 14: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

CLASSES

class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; }}

var greeter = new Greeter("world");

alert(greeter.greet());

Page 15: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

TYPES

class Animal { constructor(public name: string) { } move(meters: number) { alert(this.name + " moved " + meters + "m."); }}

class Snake extends Animal { constructor(name: string) { super(name); } move() { alert("Slithering..."); super.move(5); }}

var sam: Animal = new Snake("Sammy the Python");sam.move();

Page 16: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

GENERICS

class Greeter<T> { greeting: T; constructor(message: T) { this.greeting = message; } greet() { return this.greeting; }}

var greeter = new Greeter<string>("Hello, world");alert(greeter.greet());

Page 17: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

MODULES

module Sayings { export class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } greet() { return "Hello, " + this.greeting; } }}var greeter = new Sayings.Greeter("world");alert(greeter.greet());

Page 18: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

USAGE

npm install -g typescript

tsc helloworld.ts

<script src="helloworld.js"></script>

Or Grunt, Gulp ...

Page 19: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

TOOLSincluded in Visual StudioJetBrains (IntelliJ)VimEmacsSublime TextCATS

Page 20: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

MIGRATIONcoffee-script-to-typescript to the rescue

npm install -g coffee-script-to-typescritpt

coffee-to-typescript -cma your/files/*.coffee

Page 21: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

EXISTING LIBRARIESwe use more than 30 libraries

to be completely type-safe,you need definitions for all external libraries

DefinitelyTyped - more than 700 librarieshttps://github.com/borisyankov/DefinitelyTyped

Page 22: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

EXAMPLEjgrowl.d.ts

/// <reference path="../jquery/jquery.d.ts" />interface JQueryStatic { jGrowl: jgrowl.JGrowlStatic;}declare module jgrowl { interface JGrowlOptions { sticky?: boolean; position?: string; beforeOpen?: Function; // ... } interface JGrowlStatic { (msg: string, options?: JGrowlOptions): void; }}

$.jGrowl({ sticky: true, beforeOpen: () => { console.log("opening") } })

Page 23: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

ANGULARJS - BEFORECoffeeScript

angular.module('comments.controllers', []).directive('comments', -> restrict: 'E' scope: mount: '=' replace: yes templateUrl: 'comments/comments.html' controller: ($scope, Api) -> $scope.comments = []

$scope.load = -> Api.call Api.api.Comments.commentsRange($scope.mount.id, 0, 10) success: (res) -> $scope.comments = res.comments

$scope.load())

Page 24: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

ANGULARJS - AFTERTypeScript

/// <reference path="../app.ts" />

module comments {

interface CommentsScope extends ng.IScope { mount: k.Mount comments: Array<k.Comment> load(): void }

Page 25: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

export class CommentsCtrl { constructor($scope: CommentsScope, Api: api.Api) { $scope.comments = [];

$scope.load = () => { Api.get(Api.api.Comments.commentsRange($scope.mount.id, 0, .then((res) => { $scope.comments = res.comments; }); } }; } }

Page 26: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

export var commentsDirective: ng.IDirectiveFactory = () => { return { restrict: "E", scope: { mount: "=", appendComment: "=" }, replace: true, templateUrl: "comments/comments.html", controller: CommentsCtrl }; };}

Page 27: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

PROJECT STRUCTURE

files/comments/utils/...app.tsmain.d.tstypings.d.ts

Page 28: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

PROJECT STRUCTUREapp.ts

/// <reference path="main.d.ts" />/// <reference path="files/index.ts" />/// <reference path="comments/index.ts" />/// <reference path="utils/index.ts" />

export var module = angular.module("app", [ "gettext",

files.module.name, comments.module.name, utils.module.name ])}

Page 29: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

PROJECT STRUCTUREcomments/index.ts

/// <reference path="../app.ts" />

/// <reference path="commentsDirective.ts" />

module comments { export var module = angular.module("comments", []) .directive("comments", commentsDirective);}

Page 30: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

HOW TO TEST EVERYTHING?code coverage to the rescue

usually used for test code coverage

we can use it to see which lines were covered by manually"testing" the app

Page 31: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

LIVECOVERNot published yet. Will be on GitHub and npm.

# Generate annotated JavaScript code with Blanket.js$ simple-blanket -o app-cover.js app.js

# Run LiveCover server$ livecover -p 3000

<script src="http://localhost:3000/coverage.js"></script>

Open in your browser and start clicking like crazy.

https://localhost:3000

Page 32: How we migrated our huge AngularJS app from CoffeeScript to TypeScript

QUESTIONS?