What’s new in TypeScript 3.8
Tags: featuredposts, typescript
Published: 2020-04-01 13:38:55

---
title: What’s new in TypeScript 3.8
published: true
date: 2020-03-11 17:30:21 UTC
tags: Featured Posts,typescript
canonical_url: https://blog.logrocket.com/whats-new-in-typescript-3-8/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/t10lardojfluspgmyutf.png
---
**Written by [Esteban Herrera](https://blog.logrocket.com/author/ehrrera/)**✏️

[Typescript 3.8](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) was released on February 20th, 2020. This version includes changes to the compiler, performance, and editor.

In this post, I’m going to review five important changes to the compiler:

- Type-only imports and exports
- export * as ns syntax
- ES2020 private fields
- Top-level await
- JSDoc property modifiers

At the time of this writing, [version 3.8.3](https://github.com/microsoft/TypeScript/releases/tag/v3.8.3) is already out. So first, upgrade to the latest version:

- You can upgrade by using NPM, with commands like `npm install typescript@latest` or `npm -g upgrade typescript` (add the `-g` option if you’re using it globally)
- If you’re using Visual Studio, you can do so by downloading it [here](https://marketplace.visualstudio.com/items?itemName=TypeScriptTeam.typescript-383)
- If you’re using Visual Studio Code, you can upgrade by [modifying either the user settings or the workspace settings](https://stackoverflow.com/a/39676463/3593852)
- If you’re using Sublime Text, you can upgrade via [PackageControl](https://packagecontrol.io/packages/TypeScript)

In a terminal window, you can use the following command to confirm you’re using the latest version:

```jsx
tsc --version
```

Now let’s start by reviewing the type-only import/export feature.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## Type-only import and exports

This feature brings new syntax to import and export declarations, along with a new compiler option, `importsNotUsedAsValues`:

```jsx
import type { MyType } from "./my-module.js";
// ...
export type { MyType };
```

This gives you more control over how import statements are handled in the output, which is particularly useful when compiling using the `--isolatedModules` option, the `transpileModule` API, or Babel.

By default, TypeScript drops import statements when the imports are only used as types. For example, consider the following:

```jsx
// lion.ts
export default class Lion {}

// zoo.ts
import Lion from './lion';
let myLion: Lion;
```

This will be the output when you compile `zoo.ts`:

```jsx
// zoo.js
let myLion: Lion;
```

Usually, this won’t be a problem. But what if there’s a side-effect in the `Lion` module:

```jsx
// lion.ts
export default class Lion {}
console.log("Here's an important message about lions: ... ");
```

In this case, if the import statement is dropped from the output, the `console.log` statement will never be executed.

The import statement will also be dropped if you declare it like this:

```jsx
import {TypeA, Type2} from "./my-module";
```

However, it will be kept in the output if you declare it like this:

```jsx
import "./my-module";
```

A bit confusing, right?

Here’s another problem. In the following code, can you tell if `X` is a value or a type?

```jsx
import { X } from "./my-module.js";

export { X };
```

Knowing this might be important, Babel and TypeScript’s `transpileModule` API will output code that doesn’t work correctly if `X` is only a type, and TypeScript’s `isolatedModules` flag will generate a warning.

On the other hand, we can have a similar problem with exports, where a re-export of a type should be omitted but the compiler can’t tell that we’re just re-exporting a type during single-file Babel transpilation, for example.

In TypeScript 3.8, `import type` and `export type` make explicit the importing/exporting of types.

These are some valid ways of using them:

```jsx
import type MyType from './my-module';
import type { MyTypeA, MyTypeB } from './my-module';
import type * as Types from './my-module';

export type { MyType };
export type { MyType } from './module';
```

And here are some invalid ways:

```jsx
import { type MyType } from './my-module';
import type MyType, { FunctionA } from './my-module';

export { FunctionA, type MyType } from './my-module';
```

Keep in mind that if the type is not used as a type, the compiler will mark this as an error:

```jsx
import type Lion from './lion';
let myLion: Lion; // Valid
myLion = new Lion(); // Invalid: 'Lion' cannot be used as a value because it was imported using 'import type'.
```

When using `import type`, the behavior is to drop the import declaration from the JavaScript file, as usual. But in TypeScript 3.8, when using `import`, the behavior can be controlled with the compiler option `importsNotUsedAsValue`, which can take the values:

- `default`, to omit the import declaration
- `preserve`, to keep the import declaration, useful to execute side-effects
- `error`, like `preserve` but adds an error whenever an `import` could be written as an `import type`

This way, by adding the option `"importsNotUsedAsValue":"preserve"` to the `tsconfig.json` file:

```jsx
// tsconfig.json
{
    "compilerOptions": {
      // ...
      "importsNotUsedAsValues": "preserve"
    },
    // ...
}
```

This TypeScript code:

```jsx
import Lion from './lion';
let myLion: Lion;
```

Compiles to this JavaScript code:

```jsx
import './Lion';
let myLion;
```

## Export * as ns syntax

TypeScript supports some of the newer [ECMAScript 2020 features](https://tc39.es/ecma262/), such as `export * as namespace` declarations.

Sometimes, it’s useful to have something like this:

```jsx
import * as animals from "./animals.js";
export { animals };
```

Which exposes all the members of another module as a single member.

In ES2020, this can be expressed as one statement:

```jsx
export * as animals from "./animals";
```

TypeScript 3.8 supports this syntax.

If you configure the module for ES2020:

```jsx
// tsconfig.json
{
    "compilerOptions": {
      "module": "ES2020",
      // ...
    }
}
```

TypeScript will output the statement without modifications:

```jsx
// allAnimals.ts
export * as animals from "./animals";

// allAnimals.js
export * as animals from "./animals";
```

But if you configure the module with something earlier, for example:

```jsx
// tsconfig.json
{
    "compilerOptions": {
      "module": "ES2015",
      // ...
    }
}
```

TypeScript will output these two declarations:

```jsx
import * as animals_1 from "./animals";
export { animals_1 as animals };
```

## ES2020 private fields

ES2020 also brings a new syntax for private fields. Here’s an example:

```jsx
class Lion {
    #age: number;
    constructor(age: number) {
        this.#age = age;
    }
    getAge() {
        return this.#age;
    }
}
```

Private fields start with the `#` character, and just like fields marked with the `private` keyword, they are scoped to their containing class.

Why the `#` character? Well, apparently all the other cool characters were [already taken or could lead to invalid code](https://github.com/tc39/proposal-class-fields/blob/master/PRIVATE_SYNTAX_FAQ.md#why-was-the-sigil--chosen-among-all-the-unicode-code-points).

However, there are some rules.

First of all, you cannot use the `private` modifier and the `#` character on the same field at the same time (or the `public` modifier, although the combination wouldn’t make sense anyway).

Does this mean that the `private` modifier is going to disappear eventually?

At the time of this writing, there’s an [open discussion about this](https://github.com/microsoft/TypeScript/issues/31670), but the current plan is to leave it as it is.

So, which one should you use?

Well, it depends on how strict you want to be about privacy.

The thing with the `private` modifier is that it’s only recognized by TypeScript, which means that the access restriction is only enforced at compile-time and the private constraint will get erased from the generated JavaScript code, where the private field can be accessed without problems.

On the other hand, the `#` character will be preserved in the JavaScript code, making the field completely inaccessible outside of the class.

Another rule when using  `#`  is that private fields always have to be declared before they’re used:

```jsx
class Lion {
    constructor(age: number) {
          // Error: Property '#age' does not exist on type 'Lion'.ts
          this.#age = age;
    }
}
```

Also, notice how you have to reference the private field with `this`, otherwise, an error will be marked:

```jsx
class Lion {
    #age: number;
    // ...
    getAge() {
        // The following line throws two errors:
        // 1. Private identifiers are not allowed outside class bodies.
        // 2. Cannot find name '#age'
        return #age;
    }
}
```

In order to use fields marked with `#`, you must target ECMAScript 2015 (ES6) or higher:

```jsx
// tsconfig.json
{
    "compilerOptions": {
      "target": "ES6",
      // ...
    }
}
```

The reason is that the implementation to enforce privacy uses `WeakMap`s, which can’t be polyfilled in a way that doesn’t cause memory leaks, not all runtimes optimize the use of `WeakMap`s. In contrast, fields with the `private` modifier work with all targets and are faster.

For example, this TypeScript class:

```jsx
// lion.ts
class Lion {
    #age: number;
    constructor(age: number) {
        this.#age = age;
    }
    getAge() {
        return this.#age;
    }
}
```

Outputs this:

```jsx
// lion.js
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, privateMap, value) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to set private field on non-instance");
    }
    privateMap.set(receiver, value);
    return value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, privateMap) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to get private field on non-instance");
    }
    return privateMap.get(receiver);
};
var _age;
class Lion {
    constructor(age) {
        _age.set(this, void 0);
        __classPrivateFieldSet(this, _age, age);
    }
    getAge() {
        return __classPrivateFieldGet(this, _age);
    }
}
_age = new WeakMap();
```

And from the output of the following code, you can see the `age` property cannot be seen directly outside its class:

```jsx
const lion = new Lion(5);
console.log(lion); // 'Lion {}'
console.log(Object.keys(lion)); // '[]'
console.log(lion.getAge()); // 5
```

## Top-level await

In JavaScript, we can use `async` functions with await expressions to perform asynchronous operations:

```jsx
async function getLionInformation() {
    let response = await fetch('/animals/lion.json');
    let lion = await response.json();
    return lion;
}

getLionInformation().then((value) => console.log(value));
```

But `await` expressions are only allowed in the body of `async` functions, we cannot use them in top-level code (which can be useful when using the developer console on Chrome, for example):

```jsx
// Syntax error
let response = await fetch('/animals/lion.json');
let lion = await response.json();
console.log(lion);
```

However, [top-level await](https://tc39.es/proposal-top-level-await/) (at this time a Stage 3 proposal for ECMAScript) allows us to use `await` directly at the top level of a module or a script.

TypeScript 3.8 supports top-level await, and since files with `import` and `export` expressions are considered modules, even a simple `export {}` would be enough to make this syntax work:

```jsx
let response = await fetch('/animals/lion.json');
let lion = await response.json();
console.log(lion);

export {}
```

The only restrictions in TypeScript are that:

- The `target` compiler option must be `es2017` or above,
- The `module` compiler option must be `esnext` or `system`

## JSDoc property modifiers

JSDoc allows us to add documentation comments directly to JavaScript source code so the JSDoc tool can scan the code and generate an HTML documentation website.

But more than for documentation purposes, TypeScript uses JSDoc for type-checking JavaScript files.

This is possible due to two [compiler options](https://www.typescriptlang.org/docs/handbook/compiler-options.html):

- `allowJs`, which allows TypeScript to compile JavaScript files
- `checkJs`, which is used in conjunction with the option above and allows TypeScript to report errors in JavaScript files

TypeScript 3.8 adds support for three accessibility modifiers:

- `@public`, which means that the property can be used from anywhere (the default behavior)
- `@private`, which means that a property can only be used within the class that defines it
- `@protected`, which means that a property can only be used within the class that defines it and all the derived subclasses

For example:

```jsx
// lion.js
// @ts-check
class Lion {
    constructor() {
        /** @private */
        this.age = 5;
    }
}

// Error: Property 'age' is private and only accessible within class 'Lion'.
console.log(new Lion().age);
```

And the `@readonly` modifier, which ensures that a property is only ever assigned a value during initialization:

```jsx
// lion.js
// @ts-check
class Lion {
    constructor(ageParam) {
        /** @readonly */
        this.age = ageParam;
    }
    setAge(ageParam) {
        // Error: Cannot assign to 'age' because it is a read-only property
        this.age = ageParam;
    }
}
```

You can learn more about type checking JavaScript files [here](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html), along with the [supported JSDoc tags](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html#supported-jsdoc).

## Conclusion

In this post, you have learned about five new features in TypeScript 3.8, type-only imports and exports, the `export * as ns` syntax, ES2020 private fields, top-level await, and JSDoc property modifiers.

Of course, there are more new features.

**For the compiler** :

- Better directory watching on Linux and `watchOptions` (more info [here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-better-directory-watching-on-linux-and-watchoptions))
- “Fast and Loose” incremental checking (more info [here](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html#-fast-and-loose-incremental-checking))

**For the editor (more information [here](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/#editor-features))**:

- Refactor string concatenations (see [this](https://github.com/microsoft/TypeScript/issues/18267) and [this other issue](https://github.com/microsoft/TypeScript/issues/18267))
- Show call hierarchies (see [this issue](https://github.com/microsoft/TypeScript/issues/31863) and [this pull request](https://github.com/microsoft/vscode/pull/88168))

**And some breaking changes** :

- Stricter assignability checks to unions with index signatures
- Optional arguments with no inferences are correctly marked as implicitly `any`
- `object` in JSDoc is no longer `any` under `noImplicitAny`

You can find more information about these [breaking changes here](https://github.com/microsoft/TypeScript/wiki/Breaking-Changes).

Happy coding!

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [What’s new in TypeScript 3.8](https://blog.logrocket.com/whats-new-in-typescript-3-8/) appeared first on [LogRocket Blog](https://blog.logrocket.com).