Telerik blogs
Industry NewsT2 Dark_1200x303

TypeScript 3.9 was recently released. Check out the highlights of the new version, which focused on performance, speed and stability.


TypeScript is an open-source, strongly-typed, object-oriented, compiled language developed and maintained by Microsoft. It is a superset of the very popular JavaScript used by millions of projects (over 2 million on GitHub) that was built to bring static types to modern JavaScript. The TypeScript compiler reads in TypeScript code, which has things like type declarations and type annotations and emits a clean and readable JavaScript with those constructs transformed and removed. That code runs in any ECMAScript runtime, like your favorite browsers and NodeJS.

Version 3.9

Recently, the team at Microsoft released the latest version of TypeScript and the focus was on performance, speed and stability. This means drastically reducing bugs and app crashes, and this was also done with the help of the TypeScript community. In this article we will look into an overview of these new features and fixes.

Speed Improvements

Some popular libraries and packages like styled-components and even material-ui have been seen to have really poor compilation speed. This new version tried to fix this issue by optimizing a lot of cases, like large unions, intersections, mapped types and conditional types. So for each of these optimizations, compile times are now lower by a whopping 5–10 percent each. For material-ui, this new version of TypeScript ships with a 25 percent reduction of material-ui-styles compile time, which is a 15-second reduction, according to feedback from the team at Microsoft.

Also, I think due to the Microsoft ecosystem, the team at Visual Studio Code also gave the TypeScript team feedback about tasks like renaming a file and how long it takes. The new TypeScript version changed some things internally about language service cache file lookups to further reduce this time lapse. This all translates to a faster TypeScript all-around experience.

Improvements in Inference and Promise.all

The previous versions of TypeScript, which had promise.all and promise.race functions updated, came with a bit of regressions especially as it relates to mixing in values with null or undefined.

interface Lion {
    roar(): void
interface Seal {
    singKissFromARose(): void
async function visitZoo(lionExhibit: Promise<Lion>, sealExhibit: Promise<Seal | undefined>) {
    let [lion, seal] = await Promise.all([lionExhibit, sealExhibit]);
    lion.roar(); // uh oh
//  ~~~~
// Object is possibly 'undefined'.

In the code block above, the fact that sealExhibit has undefined type of lion is strange. With this new version of TypeScript, the code block above does not contain errors any longer. If you are using an older version with these issues with promises, please do upgrade.

ts-expect-error Comments

If you wanted to write a TypeScript library and export a function called doSomething and then in the definition, you specified that it takes two strings like this:

function doSomething(abc: string, xyz: string) {
    assert(typeof abc === "string");
    assert(typeof xyz === "string");
    // do something

This way you can always get the TypeScript squiggly red lines when you do not use the function correctly and pay attention to types. To test this behavior, let’s build a test, a unit test.

expect(() => {
    doSomething(123, 456);

Unfortunately if our tests are written in TypeScript, TypeScript will give us an error!

doSomething(123, 456);
//          ~~~
// error: Type 'number' is not assignable to type 'string'.

This is why in the new version of TypeScript, you can prefix a line with the expected error comment and TypeScript will suppress the error from being reported. However, you would not be able to misuse the comment, as TypeScript will flag a warning any time you use the comment unnecessarily.

As a quick example, the following code is okay:

// @ts-expect-error
console.log(47 * "octopus");

While this code:

// @ts-expect-error
console.log(1 + 1);

results in the error:

Unused '@ts-expect-error' directive.

If you want to read more about this, you can take a look at the [ts-expect-error]( pull request.

Awaited Will Wait Longer

Awaited Type?

The speculations are true, the new TypeScript type operator called Awaited is not shipping with this new version of TypeScript due to some more design concerns, but will be released once the team feels confident enough. You would have gotten wind of it if you followed the TypeScript issue tracker on GitHub. This new type models the way promises are unwrapped in JavaScript.

Support for “Solution Style” tsconfig.json Files

IDEs have to find the tsconfig.json file to know what configs have been set up for any given project and apply the options accordingly. So what happens when there is more than one tsconfig file and one of them is referencing the rest of them like this below:

// tsconfig.json
    "files": [],
    "references": [
        { "path": "./tsconfig.shared.json" },
        { "path": "./tsconfig.frontend.json" },
        { "path": "./tsconfig.backend.json" },

This file can be called a solution file—its only function is to manage other project files—but in some cases the server does not pick up any of the listed tsconfig files. This is exactly what we would want the server to understand, the fact that the current file can belong to one of the mentioned files in the root. The new TypeScript version fixes this by adding support to editing scenarios for these kinds of configuration.

Quick Fixes for Missing Return Expressions

This feature probably affects almost every developer I know every once in a while. People just sometimes forget the return value of their statements in a function, especially curly brackets that go with arrow functions.

// before
let f1 = () => 42
// oops - not the same!
let f2 = () => { 42 }

With the new version of TypeScript, it can now provide a quick-fix to add missing return statements, remove curly braces, or add parentheses to arrow function bodies that look suspiciously like object literals.

CommonJS Auto-Imports in JavaScript

This is my favorite update. Before now, for every import you make in a TypeScript file, it always assumes you would love to go the ECMAScript style of import and so it provides something like this during the auto-import:

import * as fs from "fs";

However, contrary to the above assumption, not everyone is targeting ECMAScript-style modules when writing JavaScript files. A lot of users still use CommonJS-style require(...) imports like so:

const fs = require("fs");

In this version 3.9, TypeScript now automatically detects the types of imports you’re using to keep your file’s style clean and consistent.

Breaking Changes

  1. Parsing Differences in Optional Chaining and Non-Null Assertions
  2. TypeScript now hoists exported declarations to the top of the file when targeting module systems like CommonJS in ES5 and above.
  3. Exports Now Use Getters for Live Bindings
  4. TypeScript 3.9 will always emit these export * declarations, making sure that it is always retained.

You can get the new version by downloading from the official TypeScript link here.


This has been a quick glance through most of the new features that the recent TypeScript 3.9 ships with and the thought process behind them. We get a faster and more intuitive version overall, and the common JS auto imports are my favorite feature. What is yours?

About the Author

Nwose Lotanna Victor

Nwose Lotanna Victor is a web technology enthusiast who documents his learning process with technical articles and tutorials. He is a freelance frontend web developer based in Lagos, Nigeria. Passionate about inclusion, community-building and movies in Africa, he enjoys learning new things and traveling.


Comments are disabled in preview mode.