Blog

Introduction to Typescript

Meta avatar Mario Romano   24. May, 2016

Typescript is an open source language developed by Microsoft and is a superset of Javascript. Let's have a look at its key features and how to install it and configure it in order to run our first typescript app.

Key Features:

  1. Easy to configure
  2. Get translated into nice Javascript
  3. Syntax and Type similar to Java
  4. Let and const, function scope variable and Immutable variable
  5. Arrow Expressions
  6. For of / For in
  7. Class and Interfaces
  8. Access Modifiers
  9. Module
  10. Ambient Declaration
  11. Decorators / Annotations

Easy to configure

You can install typescript by downloading it from the npm repository.
                
    npm install -g typescript
                
              
If you don't know if you have npm and Node.Js installed, run the following commands in your terminal to check it:
                
    node -v in
    npm -v
                
              
If you don't have Node.js and npm, download it from here: https://nodejs .org/en/download/, or Mac users can use brew :
                
    brew install node
                
              
Ubuntu users can use apt-get:
                
    curl -sL https://deb.nodesource.com/setup_5.x | sudo -E bash -
    sudo apt-get install -y nodejs
                
              
To test if the environment is fully working, clone the following typescript sample project from github https://github.com/magemello/typescript-sample-project or copy and paste the following class in a .ts file
helloworld.ts
                
    function sayHello(name) {
        return "Hello World! I'm " + name;
    }

    let user = "Mario Romano";

    document.body.innerHTML = sayHello(user);
                
then from the terminal run the typescript compiler, and if everything is setup correctly you should see a .js file appear:
                
    tsc helloworld.ts
                
              
Now your folder contains 2 helloworld files, a .ts and .js. Great, the next step is easy. To show the content of our hello world file inside a browser we need a .html that includes the generated .js file.
index.html
                
    <!DOCTYPE html>
    <html>
    <head><title>TypeScript sample project</title></head>
    <body>
    <script src="helloworld.js"></script>
    </body>
    </html>
                
              

Get translated into nice Javascript

Typescript is compiled in nice readable Javascript. Because of the compiler there are two levels of errors:
  1. Compiler errors (example 2)
  2. Runtime errors (example 3)
Unfortunately, even if typescript has types and the compiler warns you of some problems, you are not stopped from doing silly things. You can have the same errors at runtime that you would have with Javascript. An example of runtime error is the 3rd code set below that defines a variable with any type, assigns an integer to it, and then tries to execute it like a string function. As you can see, the error generated by the example number 3 is the same of the example number 1 which is written in javascript.
                
    //Javascript (1)
    var testVar = 123;
    testVar.trim();

    //Uncaught TypeError: testVar.trim is not a function(…)

    //Typescript (2)
    let testVar : string = 123;
    testVar.trim();

    //Compiler error: Type 'number' is not assignable to type 'string'.

    //Typescript (3)
    let testVar : any = 123;
    testVar.trim();

    //Runtime error: ORIGINAL EXCEPTION: TypeError: testVar.trim is not a function
                

The compilers setting are defined in the tsconfig.json. The tsconfig.json file specifies the root files and the compiler options required to compile the project. The option ModuleResolution has 2 different strategies: Classic or Node, by default is node. I recommend to use node, classic its there just for backward compatibility.
tsconfig.json
                
      {
        "compilerOptions": {
            "target": "es5",
            "module": "commonjs",
            "moduleResolution": "node",
            "emitDecoratorMetadata": true,
            "experimentalDecorators": true,
            "sourceMap": true,
            "removeComments": true,
            "declaration": true,
            "outDir": "dist"
        },
        "exclude": [
        ],
        "files": [
        ],
        "filesGlob": [
        ]
      }
                
              

Syntax and Type similar to Java

Typescript is easy to learn for a java developer, also because there are some resemblances between their types.
                
    Java                  Typescript


    Object      ====>      any

    void        ====>      void

    boolean     ====>      boolean

    array[]     ====>      array[]

    String
    char        ====>      string

    int
    long        ====>      number
    short
                

Let and const, function scope variable and Immutable variable

In Typescript is not recommended to declare variables using var. To declare a variable you can use let or const. Now let's see what is the difference between let that is block scope and var that is function scoped:
                
    //Javascript (1)
    var people = 100;
    if (people === 100) {
        var people = 200;
        //output: 200
        console.log(people);
    }

    //output: 200
    console.log(people);

    //Typescript  (2)
    let people = 100;
    if (people === 100) {
        let people = 200;
        //output: 200
        console.log(people);
    }

    //output: 100
    console.log(people);
                

Const is block scope as well and is also immutable, so after the initialization you can not change the value of the const elements, let's see what it means:
                
    const people = 123;
    if (people === 123) {
        const people = 456; // Allowed as its block scoped
    }

    people = 456; // ERROR: Left-hand side of an assignment expression cannot be a constant
                

Arrow function / Lambda

  1. You don't need to keep typing function
  2. It lexically captures the meaning of this
  3. It lexically captures the meaning of arguments
                
    //Arrow function/lambda
    let filter: (peopleName: string[], name: string) => string = function (peopleName, name) {
        for (var currentName of peopleName) {
            if (currentName === name) {
                return name;
            }
        }
    }

    //Output: mario
    console.log(filter(['mario', 'luigi', 'wario'], 'mario'));

    //Arrow function/lambda less verbose
    let filter2 = (peopleName: string[], name: string) => {
        for (var currentName of peopleName) {
            if (currentName === name) {
                return name;
            }
        }
    }

    //Output: mario
    console.log(filter2(['mario', 'luigi', 'wario'], 'mario'));
                
              
Functions with => operator keep the outside context, so you don't have to do anymore hack like "self = this" to keep the context.
                

    //Javascript
    function People() {
        this.people = 100;
        var self = this;
        this.addOne = function() {
            self.people++;
        }
    }

    var community = new People();
    setTimeout(community.addOne, 1000);

    //Output: 100
    setTimeout(function() {
        console.log(community.people);
    }, 2000);



    //Typescript
    class People {

        people:number =  100;

        addOne = () => {
            this.people++;
        }
    }

    var community = new People();
    setTimeout(community.addOne, 1000);

    //Output: 101
    setTimeout(function() {
        console.log(community.people);
    }, 2000);
                
              

For of vs For in

A nice add of EC6 that you can use also in Typescript is the "for of" that iterates over the value of an array instead of the old "for in" that iterates over the key:
            
    const people1 = ['mario', 'luigi', 'wario'];
    for (let key in people1) {
        //Output: 0, 1, 2
        console.log(key);
    }

    const people2 = ['mario', 'luigi', 'wario'];
    for (let person of people2) {
        //Output: mario, luigi, wario
        console.log(person);
    }
            

Class and Interfaces

EC6 an Typescript have introduced classes. Classes are important to structure the code in elegant object oriented way.
            
    class car {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        printColor(): void{
            console.log(this.color);
        }
    }
            

Defining an Interface we can define a common behaviour for our classes and be more explicit about the functionality they provide. A key difference between Typescript and Java is that compatibility of interfaces is determined by the properties on the interface, rather than by the inheritance chain.
            
    //Interface
    interface Cars {
        color: string;

        setColor(color: string): void;
    }

    //Implements the interface
    class Ferrari implements Cars {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        printColor(): void {
            console.log(this.color);
        }
    }

    //Execution
    let supercar = new Ferrari();
    supercar.setColor('red');

    //Output: red
    supercar.printColor();
            
You can also define abstract Class
            
    //The abstract class
    abstract class Ferrari {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        abstract printColor(): void;
    }

    //Implements the abstract class
    class FerrariGto implements Ferrari {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        printColor(): void{
            console.log(this.color);
        }
    }

    let gto = new FerrariGto();
    gto.setColor('yellow');

    //Output: yellow
    gto.printColor();
            
Or simply extend a class
            
    //Original class
    class FerrariGto {

        car: string = 'Ferrari';

        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        getColor(): string {
            return this.color;
        }

        printName(): void{
            console.log(this.color);
        }
    }

    //Extend class
    class FerrariGtoSport extends FerrariGto {

        color: string;

        printName(): void{
            console.log(this.car + " " + this.getColor() + " sport");
        }
    }

    let gtoSport = new FerrariGtoSport();
    gtoSport.setColor('red');

    //Output: Ferrari red sport
    gtoSport.printName();
            

Access Modifiers

With classes of course came also access modifiers. Be aware that the access modifiers are not translated to javascript, but you can still get benefit out of it at compile time.
Modifier public private protected
class instances yes no no
class yes yes yes
class children yes no yes

Module - Import and Export

When you write code by default it is in the global scope, so how do we define scopes in typescript?
We use the import and export keyword to do that. The export keyword creates a local scope that can be stored in a separate file. But now you need to include this external file, to do that you can use the import keyword. To load this module at runtime you have to use a module loader, you can choose between :
  1. CommonJS, server side
  2. SystemJS, wrap CommonJS for in browser use
  3. Require.js (AMD) for in browsers use
  4. Isomorphic (UMD)
  5. ECMAScript 2015 native modules (ES6)
Remember, if you want to use one of this module loader you have to include it in your project.
gto.ts
            
    export class FerrariGto {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        printColor(): void{
            console.log(this.color);
        }
    }
            
gto-sport.ts
            
    import { FerrariGto } from "./gto.ts"

    export class FerrariGtoSport extends FerrariGto {
        color: string;

        setColor(color: string): void {
            this.color = color;
        }

        printColor(): void{
            console.log(this.color + " sport");
        }
    }
            
If you want to check an example where is utilized SystemJS, you can clone this Github project from the official typescript repository: SystemJS and Typescript.

Ambient Declarations

One of the first problems that you may have is how to use an old library written in javascript with typescript?
TypeScript allows you to use existing JavaScript libraries in TypeScript using Ambient declarations. Let's say for example that in our index.html we are importing jQuery and we want to use it inside our .ts file, this is how to do it:
index.html
            
    <!DOCTYPE html>
    <html>
    <head><title>TypeScript sample project</title></head>
    <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
    <script src="helloworld.js"></script>
    </body>
    </html>
            
helloworld.ts
            
    declare var $;

    function sayHello(name) {
        return "Hello World! I'm " + name;
    }

    let user = "Mario Romano";

    $('body').html(sayHello(user));
            

Decorators / Annotations

Decorators use the form @expression, what a decorators does is add at runtime functionality to the annotated element. Decorators can be applied on:
  1. Class
  2. Method
  3. Accessor
  4. Property
  5. Parameter
  6. Metadata
Some examples:
                
    @sealed
    class Greeter {

        private _x: number;

        greeting: string;

        @format("Hello, %s")
        greeting: string;

        constructor(message: string) {
            this.greeting = message;
        }

        @enumerable(false)
        greet() {
            return "Hello, " + this.greeting;
        }

        @validate
        greetHello(@required name: string) {
            return "Hello " + name + ", " + this.greeting;
        }

        @configurable(false)
        get x() {
            return this._x;
        }
    }
                

Typings

Typings is the simple way to manage and install TypeScript definitions. It uses typings.json
            
    {
    "ambientDependencies": {
        "es6-shim": "github:DefinitelyTyped/DefinitelyTyped/es6-shim/es6-shim.d.ts#7de6c3dd94feaeb21f20054b9f30d5dabc5efabd",
        "jasmine": "github:DefinitelyTyped/DefinitelyTyped/jasmine/jasmine.d.ts#5c182b9af717f73146399c2485f70f1e2ac0ff2b"
        }
    }