Channels ▼
RSS

Design

Introduction to TypeScript


Used to develop applications for browsers, servers, phones, and the desktop, JavaScript has grown to be one of the most ubiquitous programming languages. For all its popularity, the language was not originally designed for the scale and complexity of today's use cases. One of the key features of programming languages designed for this kind of use is a type system that can enable developers to use modern tooling to refactor, navigate, and detect errors in the code as it grows and changes. TypeScript helps to fill this gap for JavaScript applications. As a superset of JavaScript, TypeScript allows programmers to use their existing JavaScript code and frameworks. TypeScript provides a flexible type system that layers well on top of existing JavaScript, which enables developers to scale their codebases more quickly and with more confidence than with vanilla JavaScript.

TypeScript Starts and Ends with JavaScript

The problem with many errors in JavaScript is that they're silent. For example, it's perfectly valid JavaScript to access an object with an arbitrary member name. If the member doesn't already exist, it will be created. The only way to detect errors like this is to come across them at runtime or by looking for them with unit tests. If we're unlucky, the error lies dormant, waiting for an unsuspecting user to find it.

function changeDirection(s) {
    if (Math.random() > 0.5) {
        s.goLet = true; // <-- Silent error   
    }
    else {
        s.goRight = true;
    }

    return s;
}

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

TypeScript allows you to annotate JavaScript with type information, and then to run this newly annotated code through a compiler. These types are completely optional, and there's no requirement that the user fully specify all the type information in a program before starting to see benefits.

To help catch the silent error mentioned earlier, we can add an annotation that describes the shape of the object changeDirection expects.

function changeDirection(s: { goLeft: boolean; goRight: boolean }) {
    if (Math.random() > 0.5) {
        s.goLet = true; // <-- The property 'goLet' does not exist on value 
                        // of type '{ goLeft: boolean; goRight: boolean; }'.
    }
    else {
        s.goRight = true;
    }

    return s;
}

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

We can also refactor this code to use interfaces. Interfaces in TypeScript work differently than interfaces in languages like C# and Java because they are implicitly satisfied by the shape an object has, rather than explicitly requiring the developer to specify that the interface is implemented.

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

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 = changeDirection(s);

These type annotations exist only during design time and are compiled away, leaving only clean JavaScript in the output with no additional overhead. These annotations allow you to build up checkable documentation about your program. As your application scales, your team can scale with it because having checkable documentation helps enforce a basic consistency in your codebase.

TypeScript Does Generics

For a type system to be beneficial, it needs to be expressive enough to describe common shapes and relationships. TypeScript's type system has been tuned to work with the patterns that are common in JavaScript code. Because of JavaScript's dynamic nature, a flexible generics type system is a key piece of supporting JavaScript programming.

To show how generics work in TypeScript, let's first start with this non-generic example:

function filterSmiths(arr: { lastName: string }[]): { lastName: string }[] {
    var result = [];
    for (var i in arr) {
        if (arr[i].lastName == "Smith") {
            result.push(arr[i]);
        }
    }
    return result;
}

When we call the filterSmiths function, we'd like to be able to update the fields and return the same type that was passed in. If we pass in an object that has more than a .lastName property, we don't want to lose those extra properties in the resulting type.

var result = filterSmiths([
    { firstName: "Bob", lastName: "Smith" }, 
    { firstName: "Sam", lastName: "Jones" }
]);  //lost .firstName in the result type

With generics, we can maintain the type information. If we naively switch our type to a generic type, though, we immediately see an error:

function filterSmiths<T>(arr: T[]): T[] {
    var result = [];
    for (var i in arr) {
        if (arr[i].lastName == "Smith") {
            result.push(arr[i]);  //error: The property 'lastName' does 
                                  //not exist on value of type 'T'.
        }
    }
    return result;
}

The compiler doesn't have sufficient information about T to know that we can access the .lastName property. To provide this, we need to create a constraint — a description of the minimal shape the generic type T has to provide.

interface HasLastName {
    lastName: string;
}

function filterSmiths<T extends HasLastName>(arr: T[]): T[] {
    var result = [];
    for (var i in arr) {
        if (arr[i].lastName == "Smith") {
            result.push(arr[i]);
        }
    }
    return result;
}


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