Tuesday, January 8, 2013

Getting started with TypeScript

The introductory TypeScript video on Channel 9 by Anders Hejlsberg (AH) is great, but how do you remember the examples he used? In this article I go through some of them.

I watched the video, and took inspiration from it. It all made sense, it flowed like poetry. But video doesn't lend itself to bookmarking or random access, never mind copying and pasting. How is one supposed to remember the concrete examples that AH uses in that video? I thought I'd simply list those examples in an article for my own benefit, mainly to serve as a personal starting reference, but naturally being a blog post I'll be glad to hear if anyone reading this gets any use out of it too.



At the very beginning of the talk, one of the most fundamental TypeScript concepts is introduced: type annotations. That's where the much-vaunted 'type safety' of TypeScript comes from, or if you like, 'static typing'. You can tell TypeScript that a property, or even a function, represents or returns a particular data type, say, a string. It can't make the resulting JavaScript type-safe, because there is no such guarantee in JavaScript, but as long as you stick to TypeScript to author your JavaScript, then you're ok. And - an important concept in TypeScript - annotations signal your intent within the code.

I've actually changed the examples AH uses slightly. Alright, I changed them a fair bit, mainly - I dunno - to make it fun for myself, and to avoid feeling like I was just parroting the great man's words. Make sense? Ok, let's go.

1. Classes

One of the first things AH does is create a class.
class Country{
    Capital: string;
    Population: number;
    SomeoneWasBorn: () => number;
}

var country = new Country();
country.Capital = "France";
So we're telling TypeScript that the Capital property is a string, Population is a number, and SomeoneWasBorn is a function that returns a number. Notice the statement completion when you type "country." - TypeScript prompts you by suggesting the 3 possible properties. Noooice! Also, if you look at the generated JavaScript, the properties don't exist, at least not in the declaration of the Country object. It's only when you new up an instance of country and assign values to any of its properties that it actually appears in the generated code. It's lazy like that.

2. Constructors

class Country{
    Capital: string;
    Population: number;

    constructor(Capital:string, Population:number){
        this.Capital = Capital;
        this.Population = Population;
    }
}

var france = new Country("Paris", 60000000);
Pretty straightforward. But what if we want to make our constructor smarter? We could have it take an initialization object, but not any old arbitrary object. We could make it take a strongly-typed object.

3. Interfaces

interface ICountryProperties {
    Capital: string;
    Population?: number;
}

class Country{
    ....
    constructor(properties:ICountryProperties) {
        this.Capital = properties.Capital
        this.Population = properties.Population;
    }
}

var ireland = new Country({Capital: "Dublin"});
The constructor now takes an initialization object that implements ICountryProperties, which tidies up that hitherto rather messy looking parameter list and provides some tooling benefits to boot, since the editor will dutifully light up if you omit any of the requisite (Population is optional) properties.

4. (Instance) Methods ...

class Country(){
    ...
    GDP() {
        return this.Population * 100)
    }
}
var france = new Country({Capital: "Paree"});
var gdp = france.GDP();
You normally would put instance methods for an object on the prototype of that object, that's just good JavaScript form, but with TypeScript you don't have to worry about any of that. If you want to you can inspect the generated code and see that that's exactly where the method has been declared. Note the extraordinarily low GDP as high achievers all flee to Belgium or Russia to avoid M. Hollande's tax.

... and how to turn them into properties by adding an accessor

get GDP() {
     return this.Population * 100;
}

var GDP = france.GDP;
Note addition of "get" and subsequent disappearance of "()";

5. Static members

class Country(){
    ...
    static numberInEU: number = 27;
}
alert("france is one of " + Country.numberInEU + " countries in the EU.");

6. Private members

class Country{
    ...
    private dirtyLittleSecret:string;
    constructor()
    {
        this.dirtyLittleSecret = "Ships weapons to Korea";
    }
}
var country = new Country();
country... // no dirtyLittleSecret property
Note that the private property doesn't appear in the statement completion when you new up a country object despite appearing to be a normal variable in the generated JavaScript. Privacy is purely a TypeScript expedient: there is no equivalent notion in JavaScript. Again, very much a case of signalling intent.

7. Automatically generated properties

class Country{
    // no need for those properties...
    constructor(public Capital:string = "Australia", public Population:number = 22863449){
        // ...and no need for those assignments
    }
}
var australia = new Country();
A nice little piece of 'syntactic sugar'. The properties get generated on the object from the arguments, and we're whacking in defaults too for good measure. So we no longer need to specify those properties on the class, nor assign to them in the constructor. Less code to type, more time to spend with our loved ones. Note that the population is the estimate for 2013.

8. Inheritance

class France extends Country{
    // France-specific stuff
}

var france = new France("Paris", 65350000);
alert(france.Capital);
Statement completion on the base properties! C├ęst formidable, non?

8. Arrow functions

class Country{
    Population: number;
    constructor(population:number) {
        this.Population = population;
        setInterval(function() ()=> {
            this.Population++;
            alert(this.Population.toString())
        }, 1000);
    }
}
var c = new Country(1300);
In this example, I new up a country with a seed population and watch as there's one born every second. The "this.Population" only works because it is lexically scoped to the outer "this", i.e. the Country class itself. If we leave the "function()" in place instead of the "() =>", "this" refers rather uselessly (for our purposes) to the Window object, ergo "this.Population" is NaN. To get around this (and indeed, this is what TypeScript has to do), you normally have to resort to 'this-aliasing':
var _this = this;
which is fugly. As well as all this there is the very real advantage that this syntax makes you look cool. Any jackass can read 'function()' and see what it means: most mortals, when confronted with lambda syntax like '() =>' quiver in fear. Not us, oh no.

9. (Internal) Modules

module World {
    export module Europe {
        export class Country {
            Population: number;
            constructor(population:number) {
                ...
            }
        }
    }
}
var ireland = new World.Europe.Country(1000);
Modules are so hot right now. If you look at the generated code for this simple example, you'll soon see that writing a module like this saves a lot of space and well, obfuscation. The TypeScript is a lot clearer.

In these examples I've deliberately avoided showing the generated JavaScript. That's because it's somewhat irrelevant what the JS ends up looking like as long as you 'model' it correctly with TypeScript - you've delegated responsibility to TypeScript to do that. Rest assured that you get clean, idiomatic JavaScript, almost certainly better than the stuff you were going to write yourself. It reminds me of the way we all relinquished micromanagement of our SQL when we made the move, maybe at first reluctantly, but eventually without giving it a second thought, to Linq?

Anyway, like I say, this was just a way for me to keep some of the 'canonical' TypeScript idioms in one spot for my own convenience. Hope you find it useful too.