Announcing TypeScript 2.0 RC

Daniel Rosenwasser

TypeScript 2.0 is almost out, and today we’re happy to show just how close we are with our release candidate! If you haven’t used TypeScript yet, check out the intro tutorial on our website to get started.

To start using the RC now, you can download TypeScript 2.0 RC for Visual Studio 2015 (which requires VS Update 3), grab it through NuGet, or use npm:

npm install -g typescript@rc

Visual Studio Code users can follow the steps here to use the RC.

This RC gives an idea of what the full version of 2.0 will look like, and we’re looking for broader feedback to stabilize and make 2.0 a solid release. Overall, the RC should be stable enough for general use, and we don’t expect any major new features to be added past this point.

On the other hand, lots of stuff has been added since 2.0 beta was released, so here’s a few features that you might not have heard about since then.

Tagged Unions

Tagged unions are an exciting new feature that brings functionality from languages like F#, Swift, Rust, and others to JavaScript, while embracing the way that people write JavaScript today. This feature is also called discriminated unions, disjoint unions, or algebraic data types. But what’s in the feature is much more interesting than what’s in the name.

Let’s say you have two types: Circle and Square. You then have a union type of the two named Shape.

interface Circle {
    kind: "circle";
    radius: number;
}

interface Square {
    kind: "square";
    sideLength: number;
}

type Shape = Circle | Square;

Notice that both Circle and Square have a field named kind which has a string literal type. That means the kind field on a Circle will always contain the string "circle". Each type has a common field, but has been tagged with a unique value.

In TypeScript 1.8, writing a function to get the area of a Shape required a type assertions for each type in Shape.

function getArea(shape: Shape) {
    switch (shape.kind) {
        case "circle":
            // Convert from 'Shape' to 'Circle'
            let c = shape as Circle;
            return Math.PI * c.radius ** 2;

        case "square":
            // Convert from 'Shape' to 'Square'
            let sq = shape as Square;
            return sq.sideLength ** 2;
    }
}

Notice we made up intermediate variables for shape just to keep this a little cleaner.

In 2.0, that isn’t necessary. The language understands how to discriminate based on the kind field, so you can cut down on the boilerplate.

function getArea(shape: Shape) {
    switch (shape.kind) {
        case "circle":
            // 'shape' is a 'Circle' here.
            return Math.PI * shape.radius ** 2;

        case "square":
            // 'shape' is a 'Square' here.
            return shape.sideLength ** 2;
    }
}

This is totally valid, and TypeScript can use control flow analysis to figure out the type at each branch. In fact, you can use --noImplicitReturns and the upcoming --strictNullChecks feature to make sure these checks are exhaustive.

Tagged unions make it way easier to get type safety using JavaScript patterns you’d write today. For example, libraries like Redux will often use this pattern when processing actions.

More Literal Types

String literal types are a feature we showed off back in 1.8, and were tremendously useful. Like you saw above, we were able to leverage them to bring you tagged unions.

We wanted to give some more love to types other than just string. In 2.0, each unique boolean, number, and enum member will have its own type!

type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
let nums: Digit[] = [1, 2, 4, 8];

// Error! '16' isn't a 'Digit'!
nums.push(16);

Using tagged unions, we can express some things a little more naturally.

interface Success<T> {
    success: true;
    value: T;
}

interface Failure {
    success: false;
    reason: string;
}

type Result<T> = Success<T> | Failure;

The Result<T> type here represents something that can potentially fail. If it succeeds, it has a value, and if it fails, it contains a reason for failure. The value field can only be used when success is true.

declare function tryGetNumUsers(): Result<number>;

let result = tryGetNumUsers();
if (result.success === true) {
    // 'result' has type 'Success<number>'
    console.log(`Server reported ${result.value} users`);
}
else {
    // 'result' has type 'Failure'
    console.error("Error fetching number of users!", result.reason);
}

You may’ve noticed that enum members get their own type too!

enum ActionType { Append, Erase }

interface AppendAction { 
    type: ActionType.Append;
    text: string;
}

interface EraseAction {
    type: ActionType.Erase;
    numChars: number;
}

function updateText(currentText: string, action: AppendAction | EraseAction) {
    if (action.type === ActionType.Append) {
        // 'action' has type 'AppendAction'
        return currentText + action.text;
    }
    else {
        // 'action' has type 'EraseAction'
        return currentText.slice(0, -action.numChars);
    }
}

Globs, Includes, and Excludes

When we first introduced the tsconfig.json file, you told us that manually listing files was a pain. TypeScript 1.6 introduced the exclude field to alleviate this; however, the consensus has been that this was just not enough. It’s a pain to write out every single file path, and you can run into issues when you forget to exclude new files.

TypeScript 2.0 finally adds support for globs. Globs allow us to write out wildcards for paths, making them as granular as you need without being tedious to write.

You can use them in the new include field as well as the existing exclude field. As an example, let’s take a look at this tsconfig.json that compiles all our code except for our tests:

{
    "include": [
        "./src/**/*.ts"
    ],
    "exclude": [
        "./src/tests/**"
    ]
}

TypeScript’s globs support the following wildcards:

  • * for 0 or more non-separator characters (such as / or \).
  • ? to match exactly one non-separator character.
  • **/ for any number of subdirectories

Next Steps

Like we mentioned, TypeScript 2.0 is not far off, but using the RC along with 2.0’s new features will play a huge part in that release for the broader community.

Feel free to reach out to us about any issues through GitHub. We would love to hear any and all feedback as you try things out. Enjoy!

0 comments

Discussion is closed.

Feedback usabilla icon