Typescript 4 is released | Features and breaking changes

Typescript 4.0 is the newest version released this year

A new version of typescript released on August 26, typescript 4. In this article, we will discuss all the new features, improvements, and breaking changes that were released with the latest Typescript version.


Who should read this article?

  1. Anyone who has a basic knowledge of JavaScript or TypeScript

  2. Frontend developers (Angular/React): Typescript is the foundation of Angular. We are already at angular version 11 and 12 is right around the corner and as angular/front-end developers it's our duty to keep ourselves up to date with typescript so that we can write code that is not really outdated and we can leverage all the new features that Microsoft and the typescript team are working on every day to make our lives better.

  3. Programmers who to be up to date


What is TypeScript?

  1. It's a language that builds on top of JavaScript.

  2. It basically adds syntax for our static types.

  3. The idea is to use typescript to basically type check your code and tell you the mistakes before you run your code.

For example, in a javascript environment you might write:

let a = "cat";

In the very next line, you can write:

a = 24; // and this is a problem!

and no one is going to stop you, not your compiler, not your IDE and neither will you know it yourself unless the code breaks because you were expecting a string and you got an integer and you will only know it post-production.

So basically the goal of typescript is to avoid those errors avoid those obvious mistakes make our code cleaner and more readable.


Summary of what’s new in TypeScript 4.0

  1. Labeled Tuple Elements

  2. Class Property Inference from Constructors

  3. Short-Circuiting Assignment Operators

  4. Better catch clause bindings


Labeled Tuple Elements

The first change I'm most excited about is label tuple elements. Those who are not new to programming know that we can pass tuples as a variable number of arguments to any function so that the function can be really dynamic.

// Javascript =>  

function addressJs(...args) {
    // ...
}

addressJs(2505, 'Sherbrooke East');
addressJs('Sherbrooke East', 2505, 10); // oops!

Although TypeScript supports it from the very beginning:

// TypeScript < 4.0 =>  

function addressTs(...args: [number, string]): void {
    // ...
}

addressTs(2505, 'Sherbrooke East'); // works

addressTs(2505, 'Sherbrooke East', true); // error
addressTs(2505); // error

In this function addressTs we had two arguments, the first a number and the second one a string.

However, it appears no different than this function below:

function addressTs2(arg0: number, arg1: string): void {
    // ...
}

addressTs2(2505, 'Sherbrooke East'); // works

It basically means that argument zero is a number and argument one is a string.

This is already much better than javascript because in javascript you won't know if we have to pass a string first and a number second.

Problem: In this example, it is not obvious to a programmer if he/she should pass an apartmentNumber as a first argument and a city as a second argument or a streetNumber or a streetName!

There can be various scenarios and there can be a lot of confusion around it.

What changed with TypeScript 4?

Now what is better is that we can name the tuples. For example, we had to define the range we can actually say that the first variable is labeled as start and the second one is end this greatly increases the readability of the code.

// In TypeScript 4.0, tuples types can now provide labels =>

type StreetAddress = [streetNumber: number,streetName: string] 
| [streetNumber: number, streetName: string, city: string];

function addressWithLabels(...address: StreetAddress): void {
    //...
}

addressWithLabels(2525, 'Sherbrooke East'); //works
addressWithLabels(2525, 'Sherbrooke East', 'Montreal'); //works

Now we can define or label our arguments. We can say that the first one should be a streetNumber and the second one should be a streetName.

This method accepts a street address. Now street address can either have a street number and street name or it can have a street number, street name, and a city. Hence, it's pretty clear now.

This will help when we have a large codebase! In big companies we have thousands of lines of code and if these things can make the life of programmers easy and better then why not implement them.

Also, we don't have to rely on documentation or comments. This kind of code is pretty readable and it would be pretty obvious what kind of arguments we need to pass.

If you really want to leverage the variable number of arguments:

type StreetAddressComplete = [
    streetNumber: number,
    streetName?: string,
    ...rest: any[]
];

In this example, we can say that the first one has to be a streetNumber which will be a number type second one has to be a string and the third one well they can have as many arguments as we want to have of any type. So we can still leverage the variable number of arguments.


Class Property Inference from Constructors

Now typescript can use the control flow to analyze what the type of these variables are in a situation like this where you define a class Square with area and sideLength but you didn't give types:

class Square {
    // Previously: implicit any!
    // Now: inferred to `number`!
    area;
    sideLength;

    constructor(sideLength: number) {
        this.sideLength = sideLength;
        this.area = sideLength ** 2;
    }
}

It analyzes that in the constructor you passed a number and then you assigned the number and did some manipulation on it and assigned it to the area as well. But typescript is smart enough to know that it's still a number and it will assume that your variables are numbers.

However, it has a limitation, for example:

class Square {
    sideLength;

    constructor(sideLength: number) {
        if (Math.random()) {
            this.sideLength = sideLength;
        }
    }

    get area() {
        return this.sideLength ** 2;
        //     ~~~~~~~~~~~~~~~
        // error! Object is possibly 'undefined'.
    }
}

In this case, it's assigned inside an if condition. So in this case typescript’s compiler won't know for sure that the type of this variable is now a number or not because there is a potential for it to be undefined.


Short-Circuiting Assignment Operators

With TypeScript 4 they announced that now we have three new assignment operators:

  1. &&=

  2. ||=

  3. ??=

So basically we could replace these assignments

a = a && b; 
a = a || b; 
a = a ?? b;

with the new ones.

Every programmer likes shorter code.

Better catch clause bindings

try {
    // ...
}
catch (x) {
    // x has type 'any' - have fun!
    console.log(x.toUpperCase());
    x++;
}

It basically means that earlier if we had a try and a catch block we don't know what type x is it can be of type any we can do an operation that was supposed to be for integers only and it won't throw an error before the code actually executes.

Now we have a type unknown that we can define that we can assign to the exception:

try {
    // ...
}
catch (e: unknown) {
    console.log(e.toUpperCase()); // compile error
    if (typeof e === "string") {
       console.log(x.toUpperCase()); 
    }
}

Now if we try to say e.toUpperCase() the compiler will throw an error because it doesn't think that type unknown has this method. So instead we have to narrow it down. If the type of exception (e) is a string only then we can call it.

This can be pretty handy because you don't want errors or exceptions in your catch code!


Breaking changes

In the end, I just want to mention that there's no major breaking change with this release so you can simply update the typescript version in your codebases and you're good to go!


I hope this article helped you to understand the latest TypeScript release.

Thanks for reading. If you have any questions, feel free to leave a response.


Resources

  1. https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/