Announcing TypeScript 2.6 RC

Daniel Rosenwasser

TypeScript 2.6 RC is now available! To get started with the latest stable version of TypeScript, you can grab it through NuGet, or use the following command with npm:

npm install -g typescript@rc

Visual Studio 2015 users (who have Update 3) can install TypeScript 2.6 RC from here, and Visual Studio 2017 users using version 15.2 or later will be able to get TypeScript by simply installing it from here.

You can get TypeScript 2.6 RC working with Visual Studio Code and Sublime Text, though the full release of TypeScript 2.6 will be available out of the box.

Let’s take a look at what TypeScript 2.6 will bring!

Contravariant function parameters with strictFunctionTypes

With TypeScript 2.6, we’re introducing a new --strict mode flag: --strictFunctionTypes. TypeScript has traditionally compared parameters in a bivariant manner. There are a number of reasons for this – modeling event handlers in the DOM, allowing a simpler model for working with arrays, and more.

In TypeScript 2.4, we tightened these checks a bit, but a few problems still remained. For example, the following would compile with no errors:

function makeLowerCase(s: string) {
    return s.toLowerCase();
}

declare let foo: Promise<string | number>;
foo.then(makeLowerCase); // Whoops!

While this is a very basic example, we re-evaluated our design decisions based on how JavaScript is written today and will be in the coming years, and felt that there were improvements we could make while accomodating the patterns described above.

Under --strictFunctionTypes, any function type that doesn’t originate from a method has its parameters compared contravariantly. That means that the above code will fail, because when we try to pass makeLowerCase with the type (s: string) => string to then which expects a function of type (onfulfilled: string | number) => ..., we’ll flip directions and try to assess whether string | number is assignable to string which it isn’t.

The compromise of increasing strictness on all function types but methods also allows TypeScript to continue modeling the above use-cases (e.g. event handlers and simpler array handling).

Covariance and contravariance probably deserve a a more thorough explanation. If you’d like to read a bit more, some of us on the team felt that this article gives a reasonable high-level explanation. You can also read up more on the original pull request. But the short version is that with --strictFunctionTypes, you’ll be able to catch many common mistakes.

Keep in mind that while --strictFunctionTypes is opt-in, it will automatically be turned on if you’re operating with the --strict flag on. This may introduce some breaks, so to disable the check with --strict on, you can specify --strictFunctionTypes false on the command line or in your tsconfig.json as so:

{
    "compilerOptions": {
        "strict": true,
        "strictFunctionTypes": false
    }
}

Error suppression comments with // @ts-ignore

Historically, we’ve avoided error suppression within TypeScript because most cases where our users asked for it could be solved through more accurate declaration files, or using a type assertion to any. However, over time, we have seen two motivating examples: migrating from JavaScript to TypeScript, and overcoming type-checks that live in legacy code.

For the former, the benefits are obvious: you want to start moving over to TypeScript, but you’ve run into a pattern that is particularly difficult to model. You could spend time trying to understand it, but maybe you’d rather get rid of it later on anyway, so you just want to avoid errors because your code will still run just fine.

The latter situation is a bit less obvious. The motivation here was that within some large organizations, dependencies across projects are updated in tandem, including TypeScript itself and @types declaration files from Definitely Typed. If any change introduces a type-checking error, someone has to fix that to avoid breaking the build. But now a question arises: who should that be on an unmaintained project? While the error is usually useful, the reality of the situation is that the code has functioned thus far and teams have finite resources.

That’s why with TypeScript 2.6 we’re bringing // @ts-ignore comments to TypeScript files. These comments suppress any errors that occur on the next line. For example, in the following code, TypeScript would ordinarily report an error about the console.log statement being unreachable. In TypeScript 2.6, the // @ts-ignore comment squelches the error entirely.

if (false) {
    // @ts-ignore: Unreachable code error
    console.log("hello");
}

While this feature is motivated by pragmatism, we encourage taking type-checking errors seriously. Our guidance here is to use suppression comments very sparingly. In situations where you do need to use these comments, we suggest leaving a trailing explanation of why the comment is necessary like in the example above.

Revised tagged template emit

Tagged template strings have been supported and type-checked in TypeScript for quite some time now. Let’s take a simple example of a tagged template string:

function foo(arg: TemplateStringsArray) {
    return arg;
}

function bar() {
    return foo `hello world`;
}

When emitting to ES3/ES5, that tagged template more or less boils down to calling foo like so:

function foo(arg) {
    return arg;
}
function bar() {
    var _a = ["hello world"];
    _a.raw = ["hello world"];
    foo(_a);
}

However, the ECMAScript specification says that every time foo `hello world` gets invoked, foo will get the same template object, so re-initializing _a in bar is incorrect. This is problematic because some libraries have an expectation that with the above example, any invocation of bar() will return the same value.

In TypeScript 2.6, within a module, tagged template string objects are now cached after the first invocation. This aligns more closely with recent revisions to the ECMAScript spec, and we will be following up with this behavior to ensure it correctly works within global code as well.

This also means that TypeScript users can use libraries like lit-html and hyperHTML which take advantage of the new behavior.

Breaking changes and deprecations

There are several minor changes that may impact your codebase. While you can read up more about them in our Breaking Changes section, please keep these in mind when upgrading.

  • Write only references are now considered unused under --noUnusedLocals and --noUnusedParameters.
  • In ambient contexts (e.g. declaration files, and declare module blocks), expressions are now disallowed in defaultexports.
  • Uninhabitable types resulting from intersections (number & string, "foo" & 42, etc.) will simplify to never when placed in a union.
  • Various changes have been made to DOM declarations in lib.d.ts.

As for deprecations, the getSymbolDisplayBuilder API is now deprecated. For simple use cases, you should be able to move to TypeChecker#symbolToString. For more advanced use cases, we plan to bring symbolToString fully up to par with SymbolDisplayBuilder in functionality by TypeScript 2.7. In a release following TypeScript 2.7, we will be be removing getSymbolDisplayBuilder.

Other features and upcoming plans

We have a more complete description of new features in our What’s New in TypeScript wiki page, and try to keep our Roadmap up to date to provide users with an idea of what’s in store. For the full release, we anticipate we’ll have some more tooling-related features that you can take advantage of.

Thanks for reading up on what we’ve got in store! We hope you enjoy this RC, and we’d love any and all feedback to ensure TypeScript 2.6 is a wonderful release.

0 comments

Discussion is closed.

Feedback usabilla icon