Getting started with TypeScript
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.