Channels ▼
RSS

.NET

Introduction to TypeScript


TypeScript Plays Well With Others

TypeScript's key benefit is that it's able to work with existing JavaScript, including both JavaScript that's part of your project and JavaScript from other libraries that your application depends on. Being able to do so fluidly means not rewriting your entire codebase to bring it into a TypeScript application. Instead, TypeScript allows you to describe objects that are visible to your app but may have been loaded outside your script. This includes JavaScript libraries like jQuery, Backbone, and AngularJS that provide utility functionality crucial to your application.

Let's take jQuery as an example. The jQuery library makes a $ symbol available at runtime that lets developers access much of the jQuery functionality. We could describe the full jQuery API to the compiler, but as a first step, all we need to do is tell the compiler that this symbol will be visible at runtime:

declare var $: any; 

This tells the compiler that the $ symbol is not being created by our application, but rather by an external script or library being run before our script is run. It also says that the type of this variable is any. With this, the compiler will allow you to access any member you wish on this variable without complaint. This enables you to get up and running quickly. While this is effective to get started, it doesn't let the compiler give us the errors and auto-completion, since the compiler lacks any type information about the $ symbol.

To get proper type-checking, we need to have the API of jQuery documented for the compiler. Luckily for us, volunteers have already been hard at work documenting the APIs of many JavaScript libraries, including jQuery. You can reach this repository on GitHub. To use these API documentation files, called .d.ts files, you include them with your project files or alongside the source files you pass to the compiler. Here's an example of the .d.ts file for jQuery:

// The jQuery instance members

interface JQuery {
    // AJAX
    ajaxComplete(handler: any): JQuery;
    ajaxError(handler: (evt: any, xhr: any, opts: any) => any): JQuery;
    ajaxSend(handler: (evt: any, xhr: any, opts: any) => any): JQuery;
    ajaxStart(handler: () => any): JQuery;
    ajaxStop(handler: () => any): JQuery;
    // ...
}

declare var $: JQueryStatic;

One way to think of .d.ts files is as the equivalent to headers in a C-based language. They act to describe the API and are a companion to the library they're describing. Similarly, in TypeScript, you use the .d.ts file to inform your tooling and load the corresponding library at runtime.

Modularity in TypeScript

As applications grow larger, it becomes ever more important to have clean separation between components. Without this separation, components morph into a tangled mess of global definitions that become increasingly fragile and more difficult to maintain and extend. Modules and namespaces allow programmers to untangle the mess and create components that can be separately maintained, extended, and even replaced — fortified with the knowledge that such changes won't affect the rest of the system.

TypeScript has two kinds of modules. The first is an internal module. Internal modules help you organize your code behind an extensible namespace, moving it out of the global namespace. This example shows the earlier changeDirection example refactored to use an internal module:

    export interface Direction {
        goLeft: boolean;
        goRight: boolean;
    }

    export function changeDirection(s: Direction) {
        if (Math.random() > 0.5) {
            s.goLeft = true;
        }
        else {
            s.goRight = true;
        }

        return s;
    }
}

var s = { goLeft: false, goRight: false };
s = RoadMap.changeDirection(s);

The second kind of module is an external module. External modules let you treat entire files as modules. The added advantage of external modules is that they can be loaded using one of the popular JavaScript module loaders. These module loaders do the additional service of removing the need to manually order your JavaScript files. Instead, module loaders automatically handle modules by loading a module's dependencies first, before loading the module. The end result is a set of modules with clean declarations of their dependencies and a compiler-enforced separation between modules.

Here is the previous example, this time refactored as two external modules:

//RoadMap.ts
export interface Direction {
    goLeft: boolean;
    goRight: boolean;
}

export function changeDirection(s: Direction) {
    if (Math.random() > 0.5) {
        s.goLeft = true;
    }
    else {
        s.goRight = true;
    }

    return s;
}

//Main.ts
import RoadMap = require("RoadMap");
var s = { goLeft: false, goRight: false };
s = RoadMap.changeDirection(s);

Notice that we now have two separate files, each importing or exporting directly from the file. The RoadMap.ts file has become a single external module denoted by the filename. In the Main.ts file, we load the RoadMap.ts using an import call. This is how we describe the dependency between these two modules. Once imported, we can interact with the module just as before.

To compile external modules, we also have to tell the compiler what kind of module loader we will be using. Currently, the compiler supports two styles: AMD/RequireJS and Node/CommonJS. To compile for AMD, we pass AMD as the module type to the compiler:

> tscRoadMap.tsMain.ts--moduleAMD

The resulting JavaScript files will then be specialized for the AMD-style module loaders like RequireJS. For example, compiling the Main.ts above outputs:

define(["require", "exports", "RoadMap"], 
    function (require, exports, RoadMap) {
        var s = { goLeft: false, goRight: false };
        s = RoadMap.changeDirection(s);
    });

You can see where the import call has become part of the list of dependencies being tracked by the module loader using the define call. TypeScript allows us to manage our files as separate external modules, with all the benefits of using module loaders, while also getting all of the type-checking benefits we expect from working in TypeScript.

Type Inference in TypeScript

Another technique that TypeScript uses to focus types on usability is type inference. Type inference has moved from its functional programming roots to being part of most programming languages today. It is a powerful tool to focus types on utility, rather than being boilerplate. In Typescript, type inference helps to infer types in some of the common JavaScript coding patterns.

The first of these patterns is to infer types during variable declaration from an initializer, a technique common with many programming languages.

var x = 3;  // x has type number 

The next example infers types in the opposite direction as the previous code, by inferring the type left-to-right. If the variable has a declared type, we can infer information about the type of the initializing expression. In this example, the parameter x in the function on the right-hand side has its type inferred to number based on the function type provided.

var f: (x: number) => string = function (x) { return x.toString(); }

The next example shows how the context of expression can also help infer its type. Here, the type of the function expression is inferred because the call in which it is created can be resolved to the function declaration, allowing inference to use the type of the declared parameter.

function f(g: (x: number) => string) { return g(3); }
f(function (x) { return x.toString(); })

We can rewrite the previous examples using lambdas to better understand how the contextual type helps maintain code readability by reducing code cruft.

function f(g: (x: number) => string) { return g(3); }
f(x => x.toString());

Because of the heavy use of patterns like callbacks in JavaScript, contextual type inference helps keep code simple without sacrificing the power of having the type information available.

Conclusion

TypeScript offers a lightweight, flexible way of working with standards-based JavaScript while enjoying the power that static type information provides. TypeScript's type system focuses on compatibility with existing JavaScript and is designed to require less effort to use than many statically typed languages. If you'd like to learn more about TypeScript, read up on it at the TypeScript homepage.


Jonathan Turner is the Program Manager for the TypeScript team at Microsoft. Amanda Silver is the Principal Director Program Manager for Client Platform Tools at Microsoft.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video