Lawrence Oputa I'm a full-stack software developer, instructor, and writer. An open-source and Linux enthusiast with a strong blend of simplicity and creativity. In my spare time, I am cheering for Chelsea.

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.

Features

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.

We made a custom demo for .
No really. Click here to check it out.

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) {
  error.message
  error.toUpperCase()
  error.toFixed()
  // ...
}

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") {
    error.toUpperCase()
  }

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

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).

Conclusion

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.

You come here a lot! We hope you enjoy the LogRocket blog. Could you fill out a survey about what you want us to write about?

    Which of these topics are you most interested in?
    ReactVueAngularNew frameworks
    Do you spend a lot of time reproducing errors in your apps?
    YesNo
    Which, if any, do you think would help you reproduce errors more effectively?
    A solution to see exactly what a user did to trigger an errorProactive monitoring which automatically surfaces issuesHaving a support team triage issues more efficiently
    Thanks! Interested to hear how LogRocket can improve your bug fixing processes? Leave your email:

    : Full visibility into your web 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 apps.

    .
    Lawrence Oputa I'm a full-stack software developer, instructor, and writer. An open-source and Linux enthusiast with a strong blend of simplicity and creativity. In my spare time, I am cheering for Chelsea.

    Leave a Reply