Announcing TypeScript 1.8

Today we are thrilled to announce the release of TypeScript 1.8. In the TypeScript 1.8 Beta release blog post, we highlighted some of the key features that are now available to TypeScript users – how JavaScript in TypeScript compilation provides a great way forward to start converting your JavaScript projects to TypeScript, improvements to JSX/TSX support, and our move to ChakraCore. This post supplements the above and details how TypeScript 1.8 provides full support for module augmentation, delivers a stronger type system with string literal types, and catches even more common bugs with smarter control flow analysis.

TypeScript 1.8 is available for Visual Studio 2015, Visual Studio 2013, NuGet (Compiler and MSBuild task), npm, and straight from the source.

Module Augmentation

TypeScript 1.8 allows users to design more modular libraries via module augmentation. This allows library authors to distribute their libraries in a piecemeal fashion. Previously, TypeScript made the assumption that modules wouldn’t change. With module augmentation, users have the ability to extend existing modules such that consumers can specify if they want to import the whole module or just a subset. This can be accomplished by simply adding an ambient module declaration and extending any existing types.

// scale.ts
export class Scale {
    weightOnEarth(mass) {}
}
// advancedScale.ts
import { Scale } from "./scale" ;
// create augmentation for Scale
declare module "./scale" {
    // Augment Core class via interface merging
    interface Scale {
        weightOnMoon(mass); // not everyone needs moon weight
    }
}
Scale.prototype.weightOnMoon = /* insert implementation */;

// consumer.ts
import { Scale } from "./scale";
import "./advancedScale";

let scale: Scale;
scale.weightOnMoon(10);  // ok

String Literal Types

It is very common for JavaScript libraries to consume a string as a configuration parameter, and usually in such cases you want to restrict the possible strings to a certain set. Take the following example of a UI element that can specify an easing method:

declare class UIElement {
    animate(options: AnimationOptions): void;
}

interface AnimationOptions {
    deltaX: number;
    deltaY: number;
    easing: string;  // Can be "ease-in", "ease-out", "ease-in-out"
}
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "out" }); // No error

With regular strings, there is no protection from this class of error. However, starting with TypeScript 1.8, strings in a type position will become string literal types. Only exact string matches are assignable to string literal types, and like any other type, they can be used in union types as well. So if we rewrite the AnimationOptions interface with string literal types, the API users now get type protection:

interface AnimationOptions {
    deltaX: number;
    deltaY: number;
    easing: "ease-in" | "ease-out" | "ease-in-out";
}

// Error: Type '"out"' is not assignable to type '"ease-in" | "ease-out" | "ease-in-out"'
new UIElement().animate({ deltaX: 100, deltaY: 100, easing: "out" });

Smarter Control Flow Analysis

It is common for complex logic that results in many branching code paths to produce hard to find bugs. TypeScript 1.8 delivers better control flow analysis to help you catch these at compile time.

Unreachable Code

JavaScript’s automatic semicolon insertion is fairly useful, but it can also lead to unwanted results at times. The example below, which actually returns undefined, highlights a common bug that is now detected by the TypeScript compiler.

function importantData() {
    return          // Automatic semicolon insertion triggered with newline
    {
        x: "thing"  // Error: Unreachable code detected.
    }
}

If for whatever reason you are experiencing too many false positives, please let us know, but you can toggle this feature off with the --allowUnreachableCode flag.

Implicit Returns

In JavaScript, reaching the end of a function with no return statement results in implicitly returning undefined. Sometimes this is the intended behavior, but often times it can be a red flag for gaps in complex logic. Unlike unreachable code, this check is off by default. To get the added safety, just add the --noImplicitReturns flag.

function isPizza(food) {   // Error: Not all code paths return a value.
    if (food === "pizza") {
        return true;
    }
    else if (food === "pie") {
        return true;
    }
    // implicitly returns `undefined`
}

TypeScript 1.8 also includes control logic for unused labels and case clause fall-through. You can read more about those here.

So Much More!

If you want the deep dive on all the new features released with 1.8, check out our what’s new page. As always, we encourage you to weigh in on the future of TypeScript by browsing our existing issues, throwing us a pull request, or just hanging out with the team on gitter.