Modern Apps

Use TypeScript in Modern Apps

Rachel Appel

Rachel AppelThe original intent of JavaScript was for Document Object Model (DOM) manipulation in a small DOM tree. Over time, however, JavaScript has become so popular that it’s now a mainstream language for any kind of app, from small marketplace apps to apps for the enterprise. As the popularity of JavaScript continues to grow, a rise in the number of tools and languages required to support its developers is inevitable, with TypeScript being one such language. 

What Is TypeScript and How Does It Work?

TypeScript is a superset of JavaScript that allows you to write and generate JavaScript code that acts more strongly typed and object-oriented, but retains the flexibility that developers love (or sometimes hate) about JavaScript. TypeScript boosts the viable range of use of JavaScript into the realm of enterprise apps, Web sites and apps where JavaScript has historically run amok due to lack of tools in this space.

Tsc.exe, an open source TypeScript compiler/code generator, is available for download at typescriptlang.org. TypeScript is a standalone compiler, so you can open up a command prompt and execute tsc.exe with the proper arguments at any time, like so:

tsc.exe --out outputfile.js inputfile.ts

You write TypeScript code, then spin it through the compiler and out comes production JavaScript. Although TypeScript is a code generator, it doesn’t output unnecessary code (as is often done for the sake of a visual design tool), mangle variable names or change the variable order. This means it’s easier to debug the final product because it’s straight-up JavaScript.

JavaScript is already an object-oriented language, but its prototypal syntax is off-putting to many developers. To solve this problem, TypeScript adds features to JavaScript such as classes and interfaces, which are proposed features of the ECMAScript 6 (ES6) standard. This makes TypeScript a code generator covered in syntactic sugar that in most cases cuts down the amount of JavaScript to maintain. For example, the following code uses the prototypal syntax:

function Animal(name, species, habitat) {
  this.name = name;
  this.species = species;
  this.habitat = habitat;
}
Animal.prototype.sayHello = function(){
  console.log("RAWR!");
}
var animal =   new Animal("Fluffy", 
  "Velociraptor ", 
  "Everywhere. Run and hide.");
animal.sayHello();

The preceding sample starts with a constructor function, a heavily used JavaScript pattern without the surrounding class definition that you’d normally see in other object-oriented languages. You define what is similar to instance members of classes inside constructor functions by using the this keyword. Outside the constructor function lies the actual prototype method that binds JavaScript methods to classes. Classes in TypeScript allow you to write the same code as in the preceding sample but with a more natural syntax, as shown in Figure 1.

Figure 1 A TypeScript Class

class Animal
{  
  name: string;
  species: string;
  habitat: string;
  constructor(name: string, species: string, habitat: string)
  {
    this.name = name;
    this.species = species;
    this.habitat = habitat;
  }
  sayhello()
  {
    Console.log("RAWR");
  }
}

To many developers the TypeScript code sample in Figure 1 is more readable than the traditional JavaScript equivalent. Clearly, the code serves as a class definition and list of members, and it shows the types of arguments. TypeScript also provides type checking, interfaces, static compile-time checking, lambda-style expressions and goodies usually found in compiled—not interpreted—languages. These extensions to the JavaScript language are beneficial, as they keep you from falling into common coding pitfalls.

Other common JavaScript challenges arise when too many variables that have public scope exist in the JavaScript global namespace, causing global namespace pollution (which seems to happen all too frequently). Fortunately, TypeScript helps in this case because it implements modules that behave like namespaces and create closures that prevent global clutter. Modules in TypeScript come in two flavors: internal and external. Internal modules contain code declared in the current file, and you must import external modules by adding ///<reference path=‘path/reference-file.ts’ /> to the top of the current code file. You can declare modules with the module keyword and need only curly brackets to close. A TypeScript module looks something like this:

module outerModule {
  "use strict";
  module innerModule {
    export function aFunction { s: string };
    export var variable = 1;
  }
}

And the emitted JavaScript looks like this:

var outerModule;
(function (outerModule) {
  "use strict";
  var innerModule;
  (function (innerModule) {
    function aFunction() { s: string }
    innerModule.aFunction = aFunction;
    innerModule.variable = 1;
  })(innerModule || (innerModule = {}));
})(outerModule || (outerModule = {}));

The preceding code creates a singleton module instance accessible from anywhere in a Windows Store app in the outerModule namespace. As you can see, modules render as anonymous functions (that is, Immediately Invoked Function Expressions, or IIFEs). Any member marked with the export directive has global scope, which is equivalent to C# members marked with the internal keyword (that is, project-wide).

Configure and Build Windows Store Apps with TypeScript

Tight integration exists between TypeScript and Visual Studio, but TypeScript ships separately, so you need to install the following tools in addition to Visual Studio 2012 Express, Pro or Ultimate editions:

After installing the extensions, you can find a TypeScript project template for Visual Studio located just underneath the JavaScript node in the New Project dialog box. This particular built-in template is an HTML client Web app template with the appropriate TypeScript assets built in, so you need do nothing else for this template to work.

Even with tight integration between Visual Studio and TypeScript, at the time of this writing there are no built-in Windows Store project templates with TypeScript (only the client Web template previously mentioned); however, the TypeScript documentation says they’re coming soon. In the meantime, if you’re building Windows Store apps with JavaScript, you can use any of the current JavaScript project templates, such as Blank, Grid, Split and more. TypeScript automatically works in them all, but you need to make some minor modifications to the project to get things going. 

To integrate TypeScript into an existing Windows Store app, you need to copy the following declaration files to a folder, for example <project-root>\tslib:

  • lib.d.ts
  • winjs.d.ts
  • winrt.d.ts

These files are available at the TypeScript download page at typescript.codeplex.com. Notice that the file extensions for the listed files end in .d.ts, in which the “d” stands for declaration. These files contain type declarations for popular frameworks like jQuery or the native Windows Runtime (WinRT) and Windows Library for JavaScript (WinJS) libraries. Figure 2contains a sample of the winjs.d.ts file, outlining commonly used WinJS methods. Notice the file is full of public declarations used by Visual Studio or other tools for compile-time checks. Because TypeScript at this point is still somewhat immature, there might be some missing, but you can add them in yourself.

Figure 2 Examining the winjs.d.ts Definition File

declare module WinJS {
  export function strictProcessing(): void;
  export module Binding {
    export function as(data: any): any;
    export class List {
      constructor (data: any[]);
      public push(item: any): any;
      public indexOf(item: any): number;
      public splice(index: number, count: number, newelems: any[]): any[];
      public splice(index: number, count: number): any[];
      public splice(index: number): any[];
      public createFiltered(predicate: (x: any) => bool): List;
      public createGrouped(keySelector: (x: any) => any, dataSelector:
         (x: any) => any): List;
        public groups: any;
        public dataSource: any;
        public getAt: any;
      }
      export var optimizeBindingReferences: bool;
  }
  export module Namespace {
    export var define: any;
    export var defineWithParent: any;
  }
  export module Class {
    export function define(constructor: any, instanceMembers: any): any;
    export function derive(
      baseClass: any, constructor: any, instanceMembers: any): any;
    export function mix(constructor: any, mixin: any): any;
  }
  export function xhr(options: { type: string; url: string; user: string;
     password: string; headers: any; data: any;
     responseType: string; }): WinJS.Promise;
  export module Application {
    export interface IOHelper {
      exists(filename: string): bool;
      readText(fileName: string, def: string): WinJS.Promise;
      readText(fileName: string): WinJS.Promise;
      writeText(fileName: string, text: string): WinJS.Promise;
      remove(fileName: string): WinJS.Promise;
    }
// More definitions

If you write WinJS apps you should be quite familiar with these method stubs, including WinJS.Binding.List and WinJS.xhr objects—all the WinRT/WinJS library stubs are at your disposal. These definition files allow IntelliSense to work in Visual Studio.

Adding a .ts file to any folder in the project causes Visual Studio to automatically create corresponding .js and .min.js (minified) companion files. TypeScript rebuilds these files each time you save a .ts file in Visual Studio.

In most of the Windows Store JavaScript templates a folder named pages contains subfolders with the collective .html, .css and .js assets needed for each page. In addition to these files are more JavaScript files in the \js folder, such as data.js, default.js and navigator.js. You must integrate TypeScript into these files by performing the following steps for each:

  1. Add declaration references to the top of each file, for example, ///<reference path=‘path/reference-file.ts’ />.
  2. Rename the .js files to a .ts extension.
  3. Modify existing JavaScript to corresponding TypeScript language constructs, that is, modules, classes, declarations and so on.

For example, to integrate \js\data.js, you need to insert references at the top of the file and convert the top-level function to a module like the code in Figure 3. If you rename data.js to data.ts, Visual Studio will create the corresponding .js and mapping files on file save.

Figure 3 TypeScript Transformation from data.js to data.ts

// Original code in data.js
(function () {
  "use strict";
  var list = new WinJS.Binding.List();
  var groupedItems = list.createGrouped(
    function groupKeySelector(item) { return item.group.key; },
    function groupDataSelector(item) { return item.group; }
  );
  // TODO: Replace the data with your real data
  // You can add data from asynchronous sources
  // whenever it becomes available
  generateSampleData().forEach(function (item) {
    list.push(item);
  });
  // ... More data-access code
})();
// The modified data.ts file
///<reference path='../ts/winjs.d.ts' />
///<reference path='../ts/winrt.d.ts' />
  module TypeScriptApp {
    "use strict";
    var list = new WinJS.Binding.List();
    var groupedItems = list.createGrouped(
      function groupKeySelector(item) { return item.group.key; },
      function groupDataSelector(item) { return item.group; }
  );
  // TODO: Replace the data with your real data.
  // You can add data from asynchronous sources whenever it becomes available
  generateSampleData().forEach(function (item) {
    list.push(item);
  });
  // ... More data-access code
}

The code in Figure 3 acts as a top-level namespace (module) using the project’s name, TypeScriptApp, sticking with standard conventions familiar to Visual Studio users.

Of course, you can always leave the current JavaScript from the templates unchanged and the code will still run as expected, but it isn’t consistent in its style or syntax, making maintenance difficult.

Visual Studio Options for TypeScript

There are settings you need to know about for optimal use of TypeScript, especially in Windows Store apps. Because there’s no need for the minified .js files in Windows Store apps, you can change the “Minify generated JavaScript” option to False in the Web Essentials tab from the Visual Studio Tools | Options dialog box and delete any that might exist. Minified files improve performance in Web sites only—not client apps—because they decrease overall required bandwidth across the Internet.

Another necessary change for use with TypeScript in Windows Store apps is to set encoding to “Re-save JS with UTF-8 BOM” (see bit.ly/ceKkYq) in the Tools | Options dialog. Setting to UTF-8 BOM (byte order mark) makes the app perform better at startup, adhering to Windows Store process lifecycle management guidelines (see my Windows 8 Special Issue column, “The Windows Store App Lifecycle,” at msdn.microsoft.com/magazine/jj660301 for more on these guidelines). In addition to performance, this encoding spec is required for passing Windows Store certification and publishing your app.

JavaScript developers know that tools like source maps are a neces­sity for debugging deployed code or source code from JavaScript generators. That’s because source maps are files that map code to other code, often between standard-sized development files and minified and combined production files that aren’t easily debugged. Set the “Generate Source Map” to True in the Visual Studio options to create source maps between TypeScript and JavaScript to enable TypeScript debugging. This option applies the --sourcemap compiler switch, which in turn creates the maps at compile time.

The TypeScript compiler by default compiles to ECMAScript 3 (ES3)-compliant code; however, you can compile to ECMAScript 5 (ES5) if you change the “Compile to ECMAScript 3” option to False in Visual Studio, which sets the tsc compiler flag --target to generate ES5 code. Developers who want to use property-style syntax or any ES5 or proposed ES6 TypeScript features should set this switch.

Extra Benefits

JavaScript is here to stay and it’s more popular than ever, so those who move to JavaScript from the land of compiled languages now have TypeScript to help them write and manage application-scale JavaScript code. JavaScript developers benefit from extra type checking and compiler services to which they don’t normally have access when writing straight-up JavaScript.


Rachel Appel is a developer evangelist at Microsoft New York City. Reach her via her Web site at rachelappel.com or by e-mail at rachel.appel@microsoft.com. You can also follow her latest updates on Twitter at twitter.com/rachelappel.

Thanks to the following technical expert for reviewing this article: Christopher Bennage (Microsoft)
Christopher Bennage (Microsoft)

Christopher Bennage is a developer at Microsoft on the Patterns & Practices team. His job is to discover, collect and encourage practices that bring developers joy. Amongst his recent technical interests are JavaScript and (casual) game development. He blogs at dev.bennage.com.

 

Rate: