Lawrence Eagles Senior full-stack developer, writer, and instructor.

What’s new in TypeScript 4.0

4 min read 1246

What’s new in TypeScript 4.0

TypeScript 4.0 is a major milestone in the TypeScript programming language and has currently leapfrogged 3.9 to become the latest stable version. In this post, we’ll look at the new features TypeScript 4.0 offers.

Getting started

To get started using 4.0, you can install it through NuGet or via NPM:

npm i typescript

You can test the code using the TypeScript playground or a text editor that supports TypeScript. I recommend using Visual Studio Code, you can get set up instructions here.


Variadic tuple types

In a nutshell, we can say TypeScript is strongly typed JavaScript. This means that it requires developers to accurately specify the format of their data types, consequently, it allows the compiler to catch type errors at compile time and therefore, give a better developer experience.

This process of accurately specifying the format of data types is known as type declaration or type definitions — it is also called typings or simple types.

With this feature, TypeScript gives types to higher-order functions such as curry, concat, and apply. These are functions that take a variable number of parameters.

Consider a small contrived example of the concat function below:

function simpleConcat(arr1, arr2) {
  return [...arr1, ...arr2];
console.log(simpleConcat([1,2,3], [5,6])) // [1, 2, 3, 5, 6]

There is currently no easy way to type this in TypeScript. The only typing strategy available currently is to write overloads.

Function or method overloading refers to a feature in TypeScript that allows us to create multiple functions having the same name but a different number of parameters or types.

Consider this:

function concat1<T>(arr1: [T], arr2: []): [T] {
    return [...arr1, ...arr2]
function concat2<T1, T2>(arr1: [T1, T2], arr2: []): [T1, T2] {
    return [...arr1, ...arr2]
function concat6<T1, T2, T3, T4, T5, T6>(arr1: [T1, T2, T3, T4, T5, T6], arr2: []): [T1, T2, T3, T4, T5, T6] {
    return [...arr1, ...arr2]
function concat7<T1, T2, T3, T4, T5, T6, A1, A2, A3, A4>(arr1: [T1, T2, T3, T4, T5, T6], arr2: [A1, A2, A3, A4]): [T1, T2, T3, T4, T5, T6, A1, A2, A3, A4] {
    return [...arr1, ...arr2]
console.log("concated 1", concat1([1], []))
console.log("concated 2", concat2([1,2], []))
console.log("concated 6", concat6([1,2,3,4,5,6], []))
console.log("concated 10", concat10([1,2,3,4,5,6], [10, 11, 12, 13]))

From the example above we can see that the number of overloads increases as the number of items in the array increases which is suboptimal. In concat6 we had to write 6 overloads even when the second array is empty and this quickly grew 10 overloads in concat10 when the second array had just 4 items.

Also, we can only get correct types for as many overloads as we write.

TypeScript 4.0 comes with significant inference improvements. It allows spread elements in tuple types to be generic and to occur anywhere in the tuple.

In older versions, REST element must be last in a tuple type. And TypeScript would throw an error if this were not the case:

// Tuple speard items are generic
function concatNumbers<T extends Number[]>(arr: readonly [Number, ...T]) {
  // return something

// spread occuring anywhere in the tuble valid in 4.0 beta.
type Name = [string, string];
type ID = [number, number];
type DevTuples = [...Name, ...Numbers]

Given these two additions, we can write a better function signature for our concat function:

type Arr = readonly any[];
function typedConcat<T extends Arr, U extends Arr>(arr1: T, arr2: U): [...T, ...U] {
    return [...arr1, ...arr2];
console.log("concated", typedConcat([1,2,3,4,5], [66,77,88,99]))

Labeled tuple elements

This is a pithy addition to TypeScript aimed at improving code readability.

Consider the code below:

type Period = [Date, Date]; // Example 1 older version

type Period = [StartDate: Date, EndDate: Date]; // Example 2 4.0 beta

function getAge(): [birthDay: Date, today: Date] {
  // ...

Previously, TypeScript developers use comments to describe tuples because the types themselves (date, number, string) don’t adequately describe what the elements represent.

From our small contrived example above, “example 2” is way more readable because of the labels added to the tuples.

When labelling tuples all the items in the tuples must be labelled.

Consider the code below:

type Period = [startDate: Date, Date]; // incorrect
type Period = [StartDate: Date, EndDate: Date]; // correct

Class property inference from constructors

In TypeScript 4.0, we can now use control flow analysis to determine the types of properties in classes when noImplicitAny is enabled. Let’s elaborate on this with some code samples.

Consider the code below:

// Compile with --noImplicitAny
class CalArea {
    Square;  // string | number
    constructor(area: boolean, length: number, breadth: number) {
        if (!area) {
            this.Square = "No area available";
        else {
            this.Square = length * breadth;

Previously, the code above would not compile if noImplicitAny is enabled. This is because property types are only inferred from direct initializations, so their types must either be defined explicitly or using an initial initializer.

However, TypeScript 4.0 can use control flow analysis of this.Square assignments in constructors to determine the types of Square.

Short-circuiting assignment operators

Currently, in JavaScript, a lot of binary operators can be combined with the assignment operator to form a compound assignment operator. These operators perform the operation of the binary operator on both operands and assigned the value to the left operand:

// compound operators
foo += bar // foo = foo + bar
foo -= bar // foo = foo - bar
foo *= bar // foo = foo * bar
foo /= bar // foo = foo/bar
foo %= bar // foo = foo % bar

The list goes on but with three exceptions:

|| // logical or operator
&& // logical and operator
?? // nullish coalescing operator

TypeScript 4.0 beta would allow us to combine these three with the assignment operator thus forming three new compound operators:

x ||= y // x || (x = y)
x &&= y // x && (x = y)
x ??= y // x ?? (x = y )

unknown on catch clause bindings

Previously, when we use the try … catch statement in TypeScript, the catch clause is always typed as any, consequently, our error-handling code lacks any type-safety which should prevent invalid operations. I will elaborate with some code samples below:

try {
  // ...
}catch(error) {
  // ...

From the code above we can see that we are allowed to do anything we want — which is really what we don’t want.

TypeScript 4.0 aims to resolve this by allowing us to set the type of the catch variable as unknown. This is safer because it’s meant to remind us to do a manual type checking in our code:

 try {
  // ...
}catch(error: unknown) {
  if(typeof error === "String") {

  if(typeof error === "number") {
  // ...

Custom JSX factories

TypeScript already supports jsxFactory compiler option, this feature, however, adds a new compiler option known as jsxFragmentFactory which enables users to customize the React.js fragment factory in the tsconfig.json:

  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "jsx": "react", // React jsx compiler option
    "jsxFactory": "createElement", // transforms jsx using createElement
    "jsxFragmentFactory": "Fragment" // transforms jsx using Fragment

The above tsconfig.json configuration transforms JSX in a way that is compatible with React thus a JSX snippet such as <article /> would be transformed with createElement instead of React.createElement. Also, it tells TypeScript to use Fragment instead of React.Fragment for JSX transformation.

Notable mentions

TypeScript 4.0 also features great performance improvements in --build mode scenarios and also allows us to use the --noEmit flag while still leveraging --incremental compiles. This was possible in older versions.

In addition, there are several editor improvements such as @deprecated JSDoc annotations recognition, smarter auto-imports, partial editing mode at startup (which aimed to speed up startup time).


TypeScript 4.0 was released on Aug 18 and all these exciting features and improvements where rolled-out with it. Without a doubt, these will improve both the developer experience and efficiency of using TypeScript. You can find more details on 4.0 here.

: Full visibility into your web and mobile apps

LogRocket is a frontend application monitoring solution 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 and mobile apps.

Lawrence Eagles Senior full-stack developer, writer, and instructor.

Leave a Reply