Prelude

As a developer, JavaScript is inevitable. Unless you're a dba or mobile app developer I suppose, but I digress.

When working with JavaScript applications, you certainly have to be using a build pipeline of some description. If you are not at least bundling and minifying you are certainly doing it wrong, and if that process isn't automated somehow then your not doing it right.

Using new language features makes JavaScript palatable, so you are already using ES6 (or newer) features, and because compatibility on the web is hard, you have a transpiler step already to convert all your code to ES5 (or ES3, I don't judge compatibility decisions). It stands to reason that Yet Another Transpiler is no big thing. Enter TypeScript and tsc.

I set this out up front so you can attack my premise alone, without throwing the baby out with the bathwater as you read the rest.

(All Examples below convert TypeScript into ES2015 (ES6) JavaScript ).

Fun with TypeScript

Let's look at some basic TypeScript. This is going to blow your mind!

First, you can initialise variables with let (which means mutable) or const(immutable) values! What a concept!

const x = 10;
let y = "string";

[^^^]TS compiles into [vvv]JS

const x = 10;
let y = "string";

Huh? Well, I guess that's just ES6, not actually Typescript.

But this will really blow your mind, Lambdas! This is a special syntax for functions. Here is both a regular function and a lambda function:

function Foo(a, b, c) {
    //Do Stuff
}

const LambdaFoo = (a, b, c) => {
    //Do Stuff
}

[^^^]TS compiles into [vvv]JS

function Foo(a, b, c) {
    //Do Stuff
}
const LambdaFoo = (a, b, c) => {
    //Do Stuff
};

Yeah! Wait, Huh? Ok, you got me again, Lambdas are just ES6 features too...

But in TypeScript, we have these cool language features. Like rest (...rest) to capture remaining arguments explicitly, destructuring ({name}, [value]) which unboxes array values & object properties, a spread (...values) operator, and, and...

const Greet = (...rest)=> {

}

const Process = ({name}, [firstOption]) =>{
    const [...rest] = [...[,1, 2, 3, 4, 5]];    
}

[^^^]TS compiles into [vvv]JS

const Greet = (...rest) => {
};
const Process = ({ name }, [firstOption]) => {
    const [...rest] = [...[, 1, 2, 3, 4, 5]];
};

Yeah but, but, but...

Objects! Classes! These are going to be awesome! Do you like C# classes? You will love this TypeScript Feature. You can have constructors, getters and setters, private fields, methods!

class Person {
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    get firstName() { return this._firstName; }
    set firstName(firstName) { this._firstName = firstName; }
    get lastName() { return this._lastName; }
    set lastName(lastName) { this._lastName = lastName; }

    Greet() {

    }
}

[^^^]TS compiles into [vvv]JS

class Person {
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }
    get firstName() { return this._firstName; }
    set firstName(firstName) { this._firstName = firstName; }
    get lastName() { return this._lastName; }
    set lastName(lastName) { this._lastName = lastName; }
    Greet() {
    }
}

Ok, well, yeah, that stuff is ES6 too... But Lambdas! You gotta use method lambdas!

class Person {
    constructor(energyLevel) {
        this.energyLevel = energyLevel;
    }
    BeAwesome = (timeOfDay) => {
        if(this.energyLevel == "High") {
            //Be Awesome Somehow
        }
    }
}

[^^^]TS compiles into [vvv]JS

class Person {
    constructor(energyLevel) {
        this.BeAwesome = (timeOfDay) => {
            if (this.energyLevel == "High") {
                //Be Awesome Somehow
            }
        };
        this.energyLevel = energyLevel;
    }
}

GOTCHA! You can't do lambda methods on classes can you, ES6? Well, actually that doesn't look much different really, that is still a lambda, just moved to a different spot. And this is still treated correctly... Then if all of this is just ES6, where does TypeScript add value?

Types!

Back in example one we used let and const. What you didn't see was that TypeScript implicitly typed those variables:

// TypeScript knows this is a Number type
const x = 10;
// and knows this is a string type
let y = "string";

To really shine, we try to use these incorrectly:

// TypeScript knows this is a Number type
const x = 10;
// and knows this is a string type
let y = "string";

// app.ts(7,1): error TS2322: Type '10' is not assignable to type 'string'.
y = x; //IDE shows squigglies (~)

This actually compiles with an error, which means our pipeline would stop and we have to fix it. Static type checking is your first Unit Test!

To make this even clearer, we can declare the types (The first TypeScript syntax of the day!)


const x:Number = 10;

let y:string = "string";

[^^^]TS compiles into [vvv]JS

const x = 10;
let y = "string";

And apart from the compiler performing type checks for us, the output just strips it all away. This implicit typing and checking is the first real exposure to true TypeScript functionality coming to life.

The Lie

I cheated earlier. While yes, all of the above did produce output, I didn't show you the compiler errors on those classes.

class Person {
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    get firstName() { return this._firstName; }
    set firstName(firstName) { this._firstName = firstName; }
    get lastName() { return this._lastName; }
    set lastName(lastName) { this._lastName = lastName; }

    Greet() {

    }
}
app.ts(3,14): error TS2339: Property '_firstName' does not exist on type 'Person'.
app.ts(4,14): error TS2339: Property '_lastName' does not exist on type 'Person'.
app.ts(7,35): error TS2339: Property '_firstName' does not exist on type 'Person'.
app.ts(8,37): error TS2339: Property '_firstName' does not exist on type 'Person'.
app.ts(9,34): error TS2339: Property '_lastName' does not exist on type 'Person'.
app.ts(10,35): error TS2339: Property '_lastName' does not exist on type 'Person'.

Not only does it type check, but it is also linting on initialisation and declaration. We can declare our private fields and the errors go away:

class Person {
    _lastName: any;
    _firstName: any;
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }

    get firstName() { return this._firstName; }
    set firstName(firstName) { this._firstName = firstName; }
    get lastName() { return this._lastName; }
    set lastName(lastName) { this._lastName = lastName; }

    Greet() {

    }
}

[^^^]TS compiles into [vvv]JS

class Person {
    constructor(firstName, lastName) {
        this._firstName = firstName;
        this._lastName = lastName;
    }
    get firstName() { return this._firstName; }
    set firstName(firstName) { this._firstName = firstName; }
    get lastName() { return this._lastName; }
    set lastName(lastName) { this._lastName = lastName; }
    Greet() {
    }
}

(any is like C#'s dynamic type: "let anything through at compile time, trust me it works at runtime". You loose intellisense though...)

No more errors. And what's more, it's just icing. It all goes away on compile.

This is the magic. This is where TypeScript shines. This is a whole lot of value, and if you are already using ES6, you could have this for nothing more than an extra pre-compile step and renaming some *.js files to *.ts. (your mileage fixing existing type errors may vary.)

Understanding how to use types and define them is a whole article in itself, and it will surely follow at some point.

This wasn't meant to show you how awesome typescript is, but instead, make you realise that the value is in the static typing and that TypeScript truly is a superset of JavaScript.