woensdag 16 april 2014

TypeScript

Het stadium waarin we statische webpagina's maakten, hebben we reeds ver achter ons gelaten. Tegenwoordig moet het allemaal nog interactiever en gelikter. Om responsive webpagina's te maken gebruiken we Ajax en steeds meer functionaliteiten laten we de client, dus in de browser, uitvoeren. Van oudsher is JavaScript hiervoor de taal naar keuze. Een tijdje is JavaScript een beetje in onbruik geraakt toen alles op de server gegenereerd moest worden. Omdat de servers het erg druk kregen en de clients steeds meer rekenkracht bezaten, ging men al snel wat taken naar de browser verleggen. Dit heeft voor een opmerkelijke comeback van JavaScript gezorgd. 

Tegenwoordig wordt JavaScript ook als programmeertaal gebruikt voor applicaties (Windows Store Applicaties) en servertoepassingen (Node.js).
Het probleem met JavaScript is echter dat het een erg lastige taal is. Bijkomend nadeel is dat JavaScript van origine niet object georiënteerd is waardoor gestructureerde applicaties lastig zijn. Met veel kunst- en vliegwerk is wel wat object georiënteerde code te bakken, maar het komt de leesbaarheid in het algemeen niet ten goede. Met TypeScript is een serieuze poging ondernomen om meer structuur en leesbaarheid aan te brengen in onze JavaScript code. In het codevoorbeeld hieronder volgt een voorbeeld van een traditioneel stukje JavaScript waarin een module MyModule wordt gemaakt met de class Person. De class Person heeft fields firstName en lastName en een methode introduce()

var MyModule = (function (my) {
    my.Person = function (first, last) {
        this.firstName = first;
        this.lastName = last;
    }
    my.Person.prototype.introduce = function () {
        alert("Hello! I am" + this.firstName + " " + this.lastName);
    }
    return my;
})(MyModule || {});

var p = new MyModule.Person("Patrick""Schmidt");
p.introduce();
Dit is dus een zeer eenvoudig voorbeeld, maar voor menigeen al abracadabra. Hetzelfde stukje ziet er in TypeScript als volgt uit:

module MyModule {
    export class Person {
        firstName: string;
        lastName: string;

        constructor(first: string, last: string) {
            this.firstName = first;
            this.lastName = last;
        }

        introduce() {
            alert("Hello! I am" + this.firstName + " " + this.lastName);
        }
    }
}

var p = new MyModule.Person("Patrick""Schmidt");
p.introduce();
In het stukje code zien we meteen een aantal keywords terug, zoals module en class. Merk ook op dat TypeScript strong typed is. Iets dat JavaScript helemaal niet is. De declaratie van variabelen, waarin de type-aanduiding achter de variabele staat, doet sterk denken aan Pascal. Dat mag geen wonder heten als je weet dat niemand minder dan Anders Hejlsberg (de maker van Turbo Pascal) een drijvende kracht achter TypeScript is. Zijn achtergrond zal echter niet de reden voor deze notatie zijn. TypeScript en JavaScript kunnen namelijk door elkaar heen gebruikt worden en dan kan een C#-achtige manier van variabelen declareren uiterst verwarrend werken. Het keyword export geeft aan dat de class Person buiten de module te gebruiken is. Dit is vergelijkbaar met het public maken van een class in een assembly.

Het grote probleem is dat een browser van dit stukje code totaal geen chocolade kan maken. Die begrijpt alleen maar JavaScript. Vandaar dat TypeScript gecompileerd moet worden. Dat kan aan de commandline gebeuren met behulp van het tsc.exe commando waarmee JavaScript gegenereerd wordt dat in de webapplicatie gebruikt kan worden, maar er zijn ook client side compilers te verkrijgen die het in de browser doen.

Tijd om eens een wat lastiger scenario te bekijken. Zoals eerder vermeld, is JavaScript niet object georiënteerd en overerving is dus niet mogelijk, maar met wat geknutsel is daar nog wel een mouw aan te passen. Er bestaan vele manieren om overerving in JavaScript te realiseren. Een populaire manier is prototype chaining waarvan hieronder een voorbeeld gegeven is.

var MyModule = (function (my) {
    my.Person = function (first, last) {
        this.firstName = first;
        this.lastName = last;
    }
    my.Person.prototype.introduce = function () {
        alert("Hello! I am " + this.firstName + " " + this.lastName);
    }

    my.Employee = function (first, last, jobtitle) {
        my.Person.prototype.constructor.call(this, first, last);
        this.jobTitle = jobtitle;
    }
    // Prototype chaining
    my.Employee.prototype = new my.Person();
    my.Employee.prototype.constructor = my.Employee;

    my.Employee.prototype.introduce = function () {
        alert("Hello! I am " + this.firstName + " " + this.lastName +
     " and I'm " + this.jobTitle);
    }

    return my;
})(MyModule || {});

var p = new MyModule.Employee("Patrick""Schmidt""developer");
p.introduce();
Dit ziet er allemaal nogal onsmakelijk uit, maar voor een geoefend JavaScripter nog steeds vrij eenvoudig. Eens kijken hoe dit er in TypeScript uit gaat zien.

module MyModule {
    export class Person {
        firstName: string;
        lastName: string;

        constructor(first: string, last: string) {
            this.firstName = first;
            this.lastName = last;
        }

        introduce() {
            alert("Hello! I am" + this.firstName + " " + this.lastName);
        }
    }

    export class Employee extends Person {
        jobTitle: string;

        constructor(first: string, last: string, jobTitle: string) {
            super(first, last);
            this.jobTitle = jobTitle;
        }

        introduce() {
            alert("Hello! I am " + this.firstName + " " + this.lastName +
                " and I'm " + this.jobTitle);
        }
    }
}

var p = new MyModule.Employee("Patrick""Schmidt""developer");
p.introduce();

Dit ziet er al veel beter uit. Het keyword extends geeft aan dat Employee erft van Person en de aanroep super() redirect first en last naar de constructor van Person (base() in C#). 

Interfaces vormen een belangrijk concept in een taal om componenten van elkaar te ontkoppelen. In typescript gaat dat als volgt:

module MyModule {

    export interface IWork {
        doSomeWork(workType: string) :number;
    }

    export class Person implements IWork
    {
        firstName: string;
        lastName: string;

        constructor(first: string, last: string) {
            this.firstName = first;
            this.lastName = last;
        }

        doSomeWork(workType: string) {
            alert(this.firstName + " is " + workType);
            return 2000;
        }
    }
}

var p = new MyModule.Person("Patrick""Schmidt");
p.doSomeWork("developing");

De interface IWork heeft een methode die een variabele van type string verwacht en een getal terug geeft. De class Person implementeert de interface middels het keyword implements. In het gegenereerde JavaScript is niets terug te vinden van dat interface. Het is dan ook alleen aanwezig voor de leesbaarheid en een ogenschijnlijke ontkoppeling. Daarnaast kunnen interfaces goed gebruikt worden voor anonieme objecten in JavaScript. Denk hierbij aan JSON objecten die een client via het netwerk ontvangt. Middels een interface kunnen de members van dit anonieme object beschreven worden.

Tot zover wat voorbeelden van TypeScript. De taal biedt nog vele andere concepten, zoals generics, statics en lambda expressions, maar daarvoor verwijs ik naar de website van TypeScript.

TypeScript heeft de toekomst. Zeker omdat niet de minste namen, zoals Hejlsberg en Gamma, een bijdrage leveren. Daarnaast is er een explosieve toename van definities voor oa jQuery, node.je en angular.js waar te nemen. Alles wijst er op dat TypeScript een hoge vlucht gaat nemen. Het zou zomaar kunnen dat we in de toekomst nog alleen maar TypeScript hoeven te kennen.

Geen opmerkingen:

Een reactie posten