Getting started with TypeScript

JavaScript

After Microsoft released TypeScript I took a look at their examples and found the idea pretty promising. I do love JavaScript. However, it has its bad parts. JavaScript doesn’t provide any built-in facilities to declare interfaces or object abstraction. There is no explicit way to have type annotations and type checking. Well, even class-based OOP is not a part of the language. Flexibility of JavaScript is incredible. So, there are tons of libraries meant to work-around these inconveniences. It hardly helps with code readability. At the same time TypeScript is a separate consistent language, which is, nonetheless, first and foremost a superset of JavaScript (as Nicholas C. Zakas puts it). You can use regular JavaScript within TypeScript and TypeScript is being transformed to JavaScript by TypeScript compiler.

I went through the specification and started writing design pattern examples on TypeScript to get a feel on the language. In one of my previous articles I had the following example of Abstract factory pattern written on JavaScript:

/*
 * @category Design Pattern by JavaScript Tutorial
 * @package AbstractFactory Sample
 * @author Dmitry Sheiko <[email protected]>
 * @link http://dsheiko.com
 */

(function() {
"use strict";
    
     // Abstract product
var AbstractWidget = function() {

    },
    
    // Concrete product
    carousel = Object.create( new AbstractWidget(), {
        render: {
            value: function() {
                console.log('Carousel widget rendered');
            }
        }
    }),
    // Concrete product
    mockCarousel = Object.create( new AbstractWidget(), {
        render: {
            value: function() {
                console.log('Mock carousel widget rendered');
            }
        }
    }),
    
    // Abstract factory
    AbstractWidgetFactory = function() {
    },
    // Concrete factory
    mockWidgetFactory = Object.create( new AbstractWidgetFactory(), {
        makeCarousel: {
            value: function() {
                return mockCarousel;
            }
        }
    }),
    // Concrete factory
    widgetFactory = Object.create( new AbstractWidgetFactory(), {
        makeCarousel: {
            value: function() {
                return carousel;
            }
        }
    }),
   
    // Client
    page = (function() {
        return {
            render: function( options ) {
                var widget,
                    factory = ( options.testing ? mockWidgetFactory : widgetFactory );

                if ( factory instanceof AbstractWidgetFactory === false ) {
                    throw new TypeError( "Argument must be an instance of AbstractWidgetFactory" );
                }
                widget = factory.makeCarousel();
                if ( widget instanceof AbstractWidget === false ) {
                    throw new TypeError( "Argument must be an instance of AbstractWidget" );
                }
                widget.render();
            }
        };
    }());

/**
 * Usage
 */
page.render({ "testing": true });
page.render({ "testing": false });

// Output
// Mock carousel widget rendered
// Carousel widget rendered

}());

The example shows how regular and mock widgets can be created by corresponding factory depending on client object (Page) configuration. I would like to have in the code AbstractWidget and AbstractFactory interfaces to be implemented by any widget and widget factory respectively. I cannot do it with Vanilla JS (pure JavaScript). So I used empty objects for the abstraction. This way I can check, at least, if the given object is an instance of AbstractWidget or AbstractFactory subtype.

The example looks slightly better when using JSA library:

/*
 * @category Design Pattern by JSA Tutorial
 * @package AbstractFactory Sample
 * @author Dmitry Sheiko <[email protected]>
 * @link http://dsheiko.com
 */

(function() {
"use strict";
    // Client
var PageClass = function( options ) {
        return {
            render: function() {
                var widget,
                    factory = ( options.testing ?
                        MockWidgetFactory.createInstance() : 
                        WidgetFactory.createInstance() );

                widget = factory.makeCarousel();
                widget.render();
            }
        }
    },
    // Abstract factory
    AbstractWidgetFactory = function() {
    },
    // Concrete factory
    MockWidgetFactory = function() {
        return {
            __extends__: AbstractWidgetFactory,
            makeCarousel: function() {
                return MockCarousel.createInstance();
            }
        }
    },
    // Concrete factory
    WidgetFactory = function() {
        return {
            __extends__: AbstractWidgetFactory,
            makeCarousel: function() {
                return Carousel.createInstance();
            }
        }
    },
    
    
    
    // Abstract product
    AbstractWidget = function() {
        return {
            render: function() {
                
            }
        }
    },
    Carousel = function() {
        return {
            __extends__: AbstractWidget,
            render: function() {
                console.log('Carousel widget rendered');
            }
        }
    },
    MockCarousel = function() {
        return {
            __extends__: AbstractWidget,
            render: function() {
                console.log('Mock carousel widget rendered');
            }
        }
    };

/**
 * Usage
 */
PageClass.createInstance({ "testing": true }).render();
PageClass.createInstance({ "testing": false }).render();

// Output
// Mock carousel widget rendered
// Carousel widget rendered

}());

It’s almost the same code, just cleaner. My intends here are obvious, inheritance is implemented almost in the same way you are used to in class-based OOP.

Now, here is the same example, but written in TypeScript:

/*
 * @category Design Pattern by TypeScript Tutorial
 * @package AbstractFactory Sample
 * @author Dmitry Sheiko <[email protected]>
 * @link http://dsheiko.com
 */

"use strict";

declare var console;

module Widget
{
    // Abstract product
    export interface AbstractWidget
    {
        render: () => void;
    }
    // Concrete product
    export class Carousel
    {
        public render(): void {
            console.log('Carousel widget rendered');
        }
    }
    // Concrete product
    export class MockCarousel
    {
        public render(): void {
            console.log('Mock carousel widget rendered');
        }
    }
}


module WidgetFactory
{
    // Abstract factory
    export interface AbstractFactory
    {
        makeCarousel: () => Widget.AbstractWidget;
    }
    // Concrete factory
    export class Mock
    {
        public makeCarousel(): Widget.AbstractWidget {
            return new Widget.MockCarousel();
        }
    }
    // Concrete factory
    export class Regular
    {
        public makeCarousel(): Widget.AbstractWidget {
            return new Widget.Carousel();
        }
    }
}

// Abstraction for configuration object
interface PageOptions
{
    testing: bool;
}

// Client
class Page
{
    private options: PageOptions;

    constructor( options: PageOptions ) {
        this.options = options;
    }

    public render(): void {
        var widget: Widget.AbstractWidget,
            factory: WidgetFactory.AbstractFactory;

        if ( this.options.testing ) {
            factory = new WidgetFactory.Mock();
        } else {
            factory = new WidgetFactory.Regular();
        }
        
        widget = factory.makeCarousel();
        widget.render();
    }
}


/**
 * Usage
 */

var testPage = new Page({ "testing": true }),
    page = new Page({ "testing": false });

testPage.render();
page.render();


// Output
// Mock carousel widget rendered
// Carousel widget rendered

As you can see the readability of this example is a way better comparing to both previous versions. We have here regular classes in the very form of class-based OOP. We have modules to encapsulate Widgets abstraction and implementers and the same for Factories. Explicit type declaration prevents ambiguous type-compatibility errors.

From my first experience I can say that writing code on TypeScript feels really good. It’s like you still on JavaScript, but improved one. Well, that is made intentionally. Microsoft really borrowed syntax from ECMAScript 4 proposal and extended it with elements of ECMAScript 6 (classes, modules). I’m not sure yet about going with TypeScript to production, but definitely, in spite of the absence of a Netbeans plugin, I’m going to use it in my private projects.