chart

Introducing TypeScript

Large-scale JavaScript application development.

Remo H. Jansen

www.remojansen.com - @RemoHJansen - blog.wolksoftware.com

Why we need
TypeScript?

"Controlling complexity
is the essence of
computer programming"

— Brian Kernighan

What we know about software complexity?

Complex !== Complicated


- Complicated implies being difficult to understand but with time and effort, ultimately knowable.


- Complex, describes the interactions between a number of entities. If the number of entities and interactions between them increase we will get to a point where it would be impossible to know and understand all of them.

chart

“As a system evolves, its complexity increases unless steps are taken to reduce it.”

- "The eight laws of software evolution" by Meir Lehman


How can we measure the complexity of software?


Chidamber & Kemerer
object-oriented metrics suite

1. Number of methods defined in class (WMC)

2. Depth of inheritance tree (DIT)

3. Number of immediate sub-classes of a class (NOC)

4. Number of classes to which a class is coupled (CBO)



Note: The Chidamber & Kemerer object-oriented metrics suite is one of
the most common ways of measuring complexity of a system but there are
many other principles and techniques which can be applied for this purpose.

How can we reduce the complexity of a system?


The SOLID principles

Single responsibility principle

Open/Close principle

Liskov substitution principle

Interface segregation principle

Dependency inversion principle



Note: Adhering to the SOLID principles is one of the most common ways to reduce
the complexity of a system but there are many other principles and techniques which
can be applied for this purpose.

The principles, when applied together, intend to make it more likely that a programmer will create a system that is easy to maintain and extend over time


So why do we
need TypeScript?


We can write SOLID OOP code with JavaScript (ES5) but it feels so unnatural .

ES6 and ES7 make writing SOLID OOP code more natural but it will be years before we can use them without having to worry about old browsers.





Note: Some JavaScript developers don't like when I say this but I think that doing things like using closures to emulate a private property is not what I would consider as natural OOP (using the "private" access modifier).

TypeScript is the future of JavaScript

TypeScript allow us to use ES6 and ES7 today!

We can use the --target compiler option to specify ECMAScript target version: 'ES3' (default), 'ES5', or 'ES6'.


ES5 class

var Person = (function () {
    function Person(name, surname) {
        this.name = name;
        this.surname = surname;
    }
    Person.prototype.greet = function () {
        return "Hello! my name is " + this.name + " " + this.surname;
    };
    return Person;
})();

ES6 class

class Person {
    public name;
    public surname;

    constructor(name, surname) {
    this.name = name;
    this.surname = surname;
    }

    public greet() {
    return `Hello! my name is ${this.name} ${this.surname}`;
    }
}

ES5 inheritance

var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};

var Animal = (function () {
    function Animal(theName) {
        this.name = theName;
    }
    Animal.prototype.move = function (meters) { /* ... */ };
    return Animal;
})();

var Snake = (function (_super) {
    __extends(Snake, _super);
    function Snake(name) {
        _super.call(this, name);
    }
    Snake.prototype.move = function (meters) {
        /* ... */
        _super.prototype.move.call(this, meters);
    };
    return Snake;
})(Animal);

ES6 inheritance

class Animal {
    name:string;
    constructor(theName: string) { this.name = theName; }
    move(meters: number = 0) { /* ... */ }
}

class Snake extends Animal {
    constructor(name: string) { super(name); }
    move(meters = 5) {
        /* ... */
        super.move(meters);
    }
}

ES5 module (RequireJS)

require([
    "./source/inversify.config",
    "./source/models/filter_state_model"
], function (kernel, FilterStateModel) {

    // code goes here ...

});

ES6 modules

import { kernel } from "./source/inversify.config";
import { FilterStateModel } from "./source/inversify.config";

// code goes here ...

We can use the --module compiler option to specify module code generation: 'commonjs', 'amd', 'system', or 'umd'.

Callbacks (ES5)

Parse.User.logIn("user", "pass", {
    success: function(user) {
    query.find({
        success: function(results) {
        results[0].save({ key: value }, {
            success: function(result) {
            // the object was saved.
            }
        });
        }
    });
    }
});

Promises (ES6)

Parse.User.logIn("user", "pass").then(function(user) {
    return query.find();
}).then(function(results) {
    return results[0].save({ key: value });
}).then(function(result) {
    // the object was saved.
});
Promise.all([Primise1, Primise2, PrimiseN]).then(function() {
    console.log("all the files were created");
});

Async Functions (ES7)

async function updateEntity() {
    var success = false;
    var user = await Parse.User.logIn(user, pass);
    if(user !== null) {
    let results = await query.find();
    success = await results[0].save({ key: value };
    }
    return success;
}

await updateEntity();
if(success) {
    // the object was saved.
}

Decorators (ES6)

@Component({
    selector: 'todo-app',
    appInjector: [
    AngularFire,
    bind(Firebase).toValue(new Firebase('https://webapi.firebaseio-demo.com/test'))
]})
@View({
    templateUrl: 'todo.html',
    directives: [NgFor]
})
class TodoApp {
    todoService: FirebaseArray;
    todoEdit: any;
    todoFilter: Boolean;

    constructor(sync: AngularFire) {
    this.todoService = sync.asArray();
    this.todoEdit = null;
    this.todoFilter = null;
    }

    editTodo($event, todo) {
    this.todoEdit = todo;
    }

}

TypeScript goes beyond
ES6 & ES7

TypeScript is not a dynamic programming language
and introduces some great features that are not available
in ES6 or ES7!


Type inference

function f() {
    return "hello";
}

Code editors can incorporate the TypeScript language service.

logo

Optional type annotations

function f(s: string) { return s; }
// It is also possible to define the type of a function using the optional type annotations
var f : (s: string) => string;
f = function(s: string) { return s; }

Interfaces

interface ISerializable { serialize() : string; }
interface IMainSkill { use() : bool; }

interface IPerson {
    name : string;
    surname : string;
    greet() : string;
}

class MockMainSkill implements IMainSkill {
    use() { return true; }
}

describe("Person Class", () => { // TDD & BDD
    it("should be able to greet", () => {
    var mainSkill = new MockMainSkill(); // SOLID: Liskov substitution principle
    var luke : IPerson = new Person("Luke", "Skywalker", mainSkill);
    expect(luke.name).to.equal("Luke");
    expect(luke.surname).to.equal("Skywalker");
    expect(luke.greet()).to.equal("Hello! my name is Luke Skywalker");
    });
}

class Person implements IPerson, ISerializable { // SOLID: Interface segregation principle
    private _skill : IMainSkill; // SOLID: Dependency inversion principle
    public name : string;
    public surname : string;
    constructor(name: string, surname: string, skill : IMainSkill) { /* ... */ }
    greet() : string { /* ... */ };
    serialize() : string { /* ... */ };
}

Generics Types

interface IValidatable {
    validate() : void;
}

class CustomCollection<T extends IValidatable> { // SOLID: Open/Close principle
    private itemArray: Array<T>;

    constructor() {
        this.itemArray = [];
    }

    Add(item: T) {
        this.validate();
        this.itemArray.push(item);
    }
    // ...
}

class User implements IValidatable{
    public name;
    validate() { if(name === null) throw new Error(); }
}

class Message implements IValidatable{
    public message;
    validate() { if(message === null) throw new Error(); }
}

var myMessage = new CustomCollection<Message>();
var myUsers = new CustomCollection<User>();
myUsers.Add(new User());
myUsers.Add(new Message()); // Error because of generic type validation

Ok... you have convinced me but I have already started my project with Vanilla JavaScript...


Good news! TypeScript is a superset of JavaScript

All your JavaScript code is already valid TypeScript code.


Want to learn more?


Pre-order now! or join our next meetup for a chance
to win a free physical copy of my upcoming book.


Thanks!

Do you have any questions?

Remo H. Jansen

www.remojansen.com - @RemoHJansen - blog.wolksoftware.com