TypeScript: Add Productivity and Manageability to your JavaScript Apps

Shayne Boyer | December 3, 2012

Large-scale Web applications, HTML5 games and mobile Web site app development are more popular than ever, and to give users the best experience, it’s a must to take advantage of core JavaScript and powerful libraries such as JQuery, JQueryUI, Knockout and others. These libraries assist in cross-browser development, help in creating the user interface and in enhancing the user experience, and (with Knockout.js) make binding data easier and faster. But you can’t talk about developing large, enterprise-level JavaScript applications without talking about Node.js—an entire server-side platform designed for running JavaScript apps. TypeScript, which can be installed as a Node.js package (using npm install -g typescript), has features that developers can use to organize and maintain enterprise-scale JavaScript applications, and that’s just one area where TypeScript provides capabilities we’ve been looking for.

TypeScript isn’t a new language. It’s a superset of JavaScript—a powerful superset—which means that all JavaScript is valid TypeScript, and what is produced by the TypeScript compiler is JavaScript. In fact, if you’re an avid JavaScript developer, you already know 90 percent of TypeScript. TypeScript provides developers with features—such as types, classes, interfaces, and modules—that some have referred to as “syntactic sugar.” But these features add capabilities and organization to what can become a cumbersome amount of code in large-scale JavaScript applications. Let’s take a closer look at each.

Note: TypeScript is an open source project, and all the information related to the project can be found at https://www.typescriptlang.org. At the time of writing, TypeScript was in Preview version 0.8.1.

Types

In JavaScript, dynamic typing can make it difficult to determine what is expected when your script calls a method or function deep in the code or in another library. In the following example, the type of x is indeterminate and can be changed without warning:

var x;

It is perfectly acceptable in JavaScript to assign “hello world” to x, perform some action, and then later in the procedure assign the number 11 to x and keep going—until, that is, the script attempts to perform a substring() or some other string-specific function, and an error is thrown at run time.

To be sure that run-time errors like this don’t occur, TypeScript lets you declare a type. Declaring a type on an object is simple, but it yields large benefits. In the following example, appending a colon and the type tells the compiler that x is a string, and any reference should allow only the appropriate actions.

var x : string;

Now, any code that assigns a number to x warns the developer prior to run time.

var x : string;
x = 11;
Error  1      Cannot convert 'number' to 'string'

TypeScript also provides type inference. In the code shown in Figure 1, no return type is defined. However, the compiler tells you that the return type is a number and permits the execution only of functions, assignments or procedures allowed for a number type.

TypeScript Enables Type Inference
Figure 1. TypeScript Enables Type Inference

TypeScript also allows type inference of the shape of an object defined within a constructor, for example, and the editor assists with IntelliSense. Here is an interface that describes the shape of an object and then sets it as the parameter type in the function:

interface myObject {
  x : number;
  y : number;
}

function add(values : myObject){
  return values.x + values.y;
}

Additionally, although the type of the object returned by the function will be inferred by the compiler, it can also be set, as shown here:

function add(values : myObject) : number {
  return values.x + values.y;
}

It is important to point out that type safety, and the enforcement of such, is a compile-time feature instead of the run-time debugging case we now have with JavaScript. I happen to like this particular feature for that reason. However, using type safety does not excuse you from having to test your application, as some may have implied.

Arrow Functions

Arrow functions (also known as Lambda) are available in TypeScript for writing function definitions and for leveraging other areas of your code. They provide some nice, succinct syntax—a style I’ve really enjoyed taking advantage of.  In the following example, a basic add function is refactored using the arrow function syntax.  (On the left is the TypeScript, and on the right the compiler-produced JavaScript for comparison.)

TypeScript JavaScript
var add = (x : number, y : number) => {  return x + y;} var add = function (x, y) {  return x + y; };

By applying the same syntax capabilities to, for example, a function that requires a callback, you can start to see how arrow functions help expedite code completion as well as reinforce type safety.

TypeScript JavaScript
function callMe(message: string, callback : (s: string) => string){  callback(message);}callMe("hello function", (s) => { alert(s); }); function callMe(message, callback) {  callback(message);}callMe("hello function", function (s) {  alert(s);});

Classes and Inheritance

Classes and inheritance are features in TypeScript that let developers handle what is done in JavaScript with a combination of the closure pattern and prototype. The JavaScript features perform just fine, but the syntax and organization of the code in TypeScript is clear and concise. Looking at what the compiler produces, it’s very close to what most developers I’ve asked would have entered themselves.

Take the following JavaScript example:

function Animal(legs, tail) {
  this.legs = legs;
  this.tail = tail;
}

Now we can create and use an object such as this one:

var cat = new Animal(4, true);

What about inheritance? In JavaScript, we can create a dog object using a prototypal approach (other inheritance patterns are available depending on your application’s need), which inherits from the Animal object definition and can now access the legs and tail properties:

var dog = Object.create(Object.getPrototypeOf(Animal));

How does this translate to TypeScript, and what is or is not more manageable about the syntax?  The following example takes the Animal object and defines its properties, constructor and types.

class Animal{
  constructor(public legs : number, public tail : bool){
  }
}

Notice here in the constructor that the type is defined for each of the properties, which prevents an incorrect assignment of the properties on creation, when the Animal object is later used in the TypeScript code. The compiler also enforces the creation of the object properly at compile time in the constructor.

The inheritance syntax in TypeScript is more of a classical model (similar to Java or C#) and falls down in my opinion, producing more JavaScript than perhaps a seasoned .js developer might desire. Consider the following example, where a Bird object is created by inheriting from the base object Animal and adds a feathers property:

var Bird = Object.create(Object.getPrototypeOf(Animal), {
  "feathers" :{value :true}
});

The Bird object that’s created has the feathers property along with the tail and legs properties defined for Animal. This is a simple bit of script to write, of course. To be sure the global namespace is not also getting our object, closure can be added like this:

var app = (function () {
  var Bird = Object.create(Object.getPrototypeOf(Animal), {
    "feathers": {
      value: true
    }
  });
  return function () {
    return Bird;
  }
})();

In comparison, the TypeScript syntax again provides type safety and (depending on your personal opinion) uses less code while providing the same functionality as the JavaScript produced by the compiler. Take notice that the legs and tail properties in the constructor in TypeScript are prefixed with “public”, which will auto-declare the properties to shortcut the physical act of typing “this.tail = tail”;. Optionally, you can also use “private” to hide the member.

TypeScript JavaScript
class Animal{  constructor(public legs : number, public tail : bool){  }}class Bird extends Animal {  constructor(public feathers : bool, legs? : number, tail? : bool) {    super(legs, tail);  }} var __extends = this.__extends || function (d, b) {  function __() { this.constructor = d; }  __.prototype = b.prototype;  d.prototype = new __();}var Animal = (function () {  function Animal(legs, tail) {    this.legs = legs;    this.tail = tail;  }  return Animal;})();var Bird = (function (_super) {  __extends(Bird, _super);  function Bird(feathers, legs, tail) {    _super.call(this, legs, tail);    this.feathers = feathers;  }  return Bird;})(Animal);

At first glance you can see that the TypeScript has fewer lines of code than the JavaScript resulting from the compiler or than the previous hand-cranked script examples. However, in the end the functionality is the same in either set of JavaScript.  What TypeScript provides here is strong typing of the properties, optional parameters on the constructors (appending “?”), and consistency with the JavaScript produced by the compiler.

Another approach is to allow for a default value in the constructor—say, for example, setting the tail property to true if it’s not defined. You can make this change simply by adding “ = true “ after the declaration of the tail property in either the Animal class or the Bird class, and the compiled JavaScript is changed accordingly.

TypeScript JavaScript
class Bird extends Animal {  constructor(public feathers : bool, legs? : number, tail? : bool = true) {    super(legs, tail);  }} var Bird = (function (_super) {  __extends(Bird, _super);  function Bird(feathers, legs, tail) {    if (typeof tail === "undefined") { tail = true; }    _super.call(this, legs, tail);    this.feathers = feathers;  }  return Bird;})(Animal);

Modules

Modules in TypeScript are a way to organize and encapsulate code with similar responsibility. They serve a function something like a namespace in .NET. JavaScript has no formal namespace construct, and although it is possible to span namespaces across multiple .js files, doing so can become cumbersome to manage. Modules help overcome this. Look at the following simple example, which wraps the Animal class within a module named Farm.

TypeScript JavaScript
module Farm {  class Animal{    weight : number;    private full : number;    constructor(public legs : number, public
        tail : bool, full : number = 10){      this.weight = 1;      this.full = full;    }    eat(){      if (this.weight != this.full){        this.weight += 1;      } else {        throw "Animal is full";      }    }    play(){      this.weight -= 1;    }  }  export class Dog extends Animal {    constructor(full? : number = 10) {      super(4, true, full);    }  }}
var __extends = this.__extends || function (d, b) {  function __() { this.constructor = d; }  __.prototype = b.prototype;  d.prototype = new __();}var Farm;(function (Farm) {  var Animal = (function () {    function Animal(legs, tail, full) {      if (typeof full === "undefined") { full        = 10; }      this.legs = legs;      this.tail = tail;      this.weight = 1;      this.full = full;    }      Animal.prototype.eat = function () {        if(this.weight != this.full) {          this.weight += 1;          } else {          throw "Animal is full";          }        };      Animal.prototype.play = function () {        this.weight -= 1;      };      return Animal;  })();     var Dog = (function (_super) {    __extends(Dog, _super);    function Dog(full) {      if (typeof full === "undefined") { full        = 10; }      _super.call(this, 4, true, full);   }   return Dog;  })(Animal);  Farm.Dog = Dog;   })(Farm || (Farm = {}));

Farm is the namespace and provides the encapsulation of the Animal object and the organization of the code if we define additional functions within the Farm namespace. To put this in the context of the JavaScript global namespace, Animal is not in the global namespace—only Farm is--and access to Dog is always accomplished through Farm.Dog and not through Dog directly.

Modules are also auto-merged, so if another file or block of code later in the same file is also wrapped within the module named Farm, all functions or classes marked with “export” are accessible through the Farm namespace.

The compiler produces nicely formatted JavaScript that represents namespacing through the module pattern, which protects state and hides the functionality of objects not marked as public or with the keyword “export”.

Wrapping Up

Keep in mind that TypeScript is still in beta. In TypeScript, the core concepts are designed to add productivity and manageability to your JavaScript apps. After using TypeScript actively for about a month, I’ve found some features of the syntax that I really enjoy, such as creating namespaces through modules and using arrow functions and type safety. Using types is especially powerful when you’re referencing other libraries though definition files (*.d.ts), and this is the area I see as most helpful from a productivity perspective.

In the next article in this series, I’ll show how to use existing script libraries and how to create definition files, and I’ll dig into some of the more advanced features of TypeScript.

About the Author

Shayne Boyer (Telerik MVP, Nokia Developer Champion, Microsoft MCP, and INETA speaker) is a solutions architect in Orlando, Florida, and has been developing Microsoft-based solutions for the past 15 years. Over the last 10 years, he’s focused on productivity and performance in large-scale Web applications. In his spare time he runs the Orlando Windows Phone and Windows 8 User Group, and he blogs the latest technology at https://www.tattoocoder.com.