Working with Properties
TypeScript allows you to use member accessor declarations. For example, the following lines add a public Color
property with the get
and set
member accessor that make it possible to change and retrieve the value for the underlying private color
member:
//// TypeScript code class MulticolorBadAlien extends BadAlien { private color: string; constructor(public name: string, public angle: number, public bonus: number, public rotationSpeed: number, initialColor: string) { super(name, angle, bonus, rotationSpeed); this.color = initialColor; } get Color(): string { return this.color; } set Country(value: string) { this.color = value; }
The TypeScript compiler generates the following JavaScript code, which calls Object.defineProperty
for both the get
and set
accessors:
//// JavaScript code generated by the TypeScript compiler Object.defineProperty(MulticolorBadAlien.prototype, "Color", { get: function () { return this.color; }, enumerable: true, configurable: true }); Object.defineProperty(MulticolorBadAlien.prototype, "Country", { set: function (value) { this.color = value; }, enumerable: true, configurable: true });
Working with Type Assertions
TypeScript allows you to assert a type for an expression. For example, the following createBadAlien
function returns an instance of the BadAlien
class. However, createBadAlien
receives the desired alienType
as a parameter, so it might return a new instance of the MulticolorBadAlien
class (a subclass of BadAlien
). Because you know that the call to createBadAlien
with multicolor
as the value for alienType
will return an instance of MulticolorBadAlien
, you can use <MulticolorBadAlien>
to treat the result as a MulticoreBadAlien
. It is a good idea to use the instanceof
operator to check the type before using type assertions.
//// TypeScript code function createBadAlien(alienType: string, name: string, angle: number, bonus: number, rotationSpeed: number, initialColor: string): BadAlien { if (alienType === "multicolor") { return new MulticolorBadAlien(name, angle, bonus, rotationSpeed, initialColor); } } var multicolorBadAlien = <MulticolorBadAlien> createBadAlien("multicolor", "DrDobbsMulticolorBadAlien", 0, 5000, 5, "blue");
Organizing the Code with Modules
TypeScript allows you to express the common JavaScript module pattern. You can organize your classes in modules, and decide which classes you want to be accessible from outside the module. This way, you can have internal classes within the modules that will be hidden to the module consumers.
The module
keyword makes it possible to define modules with names, and the export
keyword specifies which module members will be accessible from outside the module. The following lines define two modules: AliensCore
and AliensCharacters
. AliensCore
exports the Alien
interface, and AliensCharacters
exports the BadAlien
class that inherits from AliensCore.Alien
:
//// TypeScript code module AliensCore { export interface Alien { name: string; angle: number; bonus: number; rotationSpeed: number; } } module AliensCharacters { export class BadAlien implements AliensCore.Alien { private damagePower: number; constructor(public name: string, public angle: number, public bonus: number, public rotationSpeed: number) { this.damagePower = 0.1 * bonus; } increaseSpeed() { this.rotationSpeed += 1; } } }
Because the interface doesn't have a representation in the JavaScript code, the TypeScript compiler only generates the code for the AliensCharacters
module:
//// JavaScript code generated by the TypeScript compiler var AliensCharacters; (function (AliensCharacters) { var BadAlien = (function () { function BadAlien(name, angle, bonus, rotationSpeed) { this.name = name; this.angle = angle; this.bonus = bonus; this.rotationSpeed = rotationSpeed; this.damagePower = 0.1 * bonus; } BadAlien.prototype.increaseSpeed = function () { this.rotationSpeed += 1; }; return BadAlien; })(); AliensCharacters.BadAlien = BadAlien; })(AliensCharacters || (AliensCharacters = {}));
Modules make it easy to organize classes, interfaces, and functions for better maintainability. You can also use nested modules by exporting the modules declared within a module.
The import
keyword allows you to shorten names. For example, the following line allows you to create an alias for a long module name: SpaceGame.Core.AliensCharacters
. This way, you can reference the module with the alias: SCA
.
import SCA = SpaceGame.Core.AliensCharacters; var firstBadAlien = new SCA.BadAlien("DrDobbsBadAlien", 0, 5000, 5);
TypeScript also supports external modules with two possible patterns of JavaScript code generation:
- CommonJS Modules pattern: This is the default module code generation pattern. You can use
--module commonjs
in the command-line. - AMD (short for Asynchronous Module Definition): You can use
--module amd
in the command-line.
You can select your desired code generation pattern with a compiler setting configured in the dialog box shown in Figure 7, or with the appropriate command-line option. In TypeScript, you simply need to use the import
keyword to assign an alias for the external module.
Figure 7: Selecting your code generation pattern.
For example, the following line will load the characters
module from the characters.ts
file and make it possible to reference it with the characters
name.
import characters = module("characters"); var newCharacter = new characters.SuperBadAlien();
You can also add a dependency on a TypeScript source file by adding a comment that includes a reference path. For example, the following line adds a dependency to the TypeScript declarations to use Node.js:
/// <reference path="node.d.ts"/>
Working with Arrow Function Expressions
An arrow function expression is a feature planned for ECMAScript 6 that makes it possible to omit the function
keyword and has a lexical scoping of this
. TypeScript includes support for these expressions. When you use an arrow function expression, the code preserves the this
of the enclosing context, making these expression is useful for writing callbacks. The following code uses an arrow function expression to pass a parameter to the setTimeout
function and access this.score
:
//// TypeScript code var game = { score: 0, startTimeout: function () { setTimeout(() => { alert("Your score was: " + this.score.toString()); }, 2000); } }; game.startTimeout(); game.score++; game.score++;
TypeScript generates the JavaScript code that makes the callback have the same this
as the surrounding startTimeout
. The following lines show how the code defines a _this
variable, assigns it the value of this
, and then uses _this
within the callback.
//// JavaScript code generated by the TypeScript compiler var game = { score: 0, startTimeout: function () { var _this = this; setTimeout(function () { alert("Your score was: " + _this.score.toString()); }, 2000); } }; game.startTimeout(); game.score++; game.score++;
Working with Declaration Files
If you want to work with jQuery in TypeScript with types and auto-completion, you have to reference a declaration file that includes the typing for this library. You can download the jquery.d.ts file from the TypeScript samples here. Then, you just need to add the reference path in your TypeScript file:
/// <reference path="jquery.d.ts" />
You can also find any number of declaration files for popular JavaScript libraries in the repository for Definitely Typed. Multiple contributors generated the different declaration files and you will find declarations for popular libraries such as Backbone.js, GoogleMaps and Knockout.js.
Conclusion
TypeScript simplifies organizing and refactoring JavaScript code. It also adds value to existing JavaScript code by allowing you to take advantage of language features to reduce the number of errors and problems common with JavaScript. However, you will definitely need Visual Studio 2012 in order to take full advantage of TypeScript. If Visual Studio 2012 is your main development environment and you work with many JavaScript files in your projects, you should start taking advantage of some of its features. TypeScript is still new and there are doubtless improvements to come, but it is already a handy resource when you have large amounts of JavaScript that require better organization.
Gaston Hillar is an expert in Windows-based development and is a frequent contributor to Dr. Dobb's.
Related Article
Microsoft TypeScript: The Lay of the Land