Telerik blogs
Industry NewsT2 Light_1200x303

Now is the best time to adopt TypeScript. Let's take a look at the recent 4.0 release and see why!

TypeScript

TypeScript, a superset of JavaScript, is an open-source, strongly-typed, object-oriented compiled language developed and maintained by the team at Microsoft. TypeScript ensures that even before you save your code it is type-checked, and then after that even strips away all the types to have a clean and legible JavaScript. It is also used in your favorite editors to do things like auto-complete and refactoring. TypeScript has one of the fastest-growing developer communities around, and with the new version 4.0, there are no major breaking changes, so this is the best time to get started with TypeScript.

Some Background

TypeScript has recorded tremendous growth in the past few months with monthly download numbers on npm surpassing 50 million in July 2020. It is clear that there is great love for TypeScript. Last year the The State of JavaScript developer survey had thousands of developers vote TypeScript as their second most loved language, and almost 90% say they will use it again.

Installing TypeScript

For this new version, TypeScript developers can get it through NuGet at this link, or use the Node package manager with this command:

npm install -D typescript

IDE support for TypeScript for this version is already on Visual Studio and even Visual Studio Code by installing the Insiders version on the link here.

This version of the language focused on a deeper dive into expressivity, productivity, and scalability and a lot of improvements on the experience around tuples. Now let’s take a look at an overview of some of the changes this new version shipped with.

Short-Circuiting Assignment Operators

In JavaScript you can rewrite the following commands as below for simple operations like addition, subtraction, division and multiplication:

// Addition
// a = a + b
a += b;
// Subtraction
// a = a - b
a -= b;
// Multiplication
// a = a * b
a *= b;
// Division
// a = a / b
a /= b;
// Exponentiation
// a = a ** b
a **= b;
// Left Bit Shift
// a = a << b
a <<= b;

TypeScript supports these too and now that ECMAScript is also progressing and adding new assignment operators like &&=, ||=, and ??= this new version shipped with support for them too. A community member Wenlu Wang contributed this new feature.

These operators are good for swapping and even complying to the DRY principle. An example for use would be where a user might write code like the following:

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

Or even an if code block like this one below:

// could be 'a ||= b'
if (!a) {
    a = b;
}

To start using it, consider running this example below to see how that differs from always performing the assignment.

const obj = {
    get prop() {
        console.log("getter has run");
        // Replace me!
        return Math.random() < 0.5;
    },
    set prop(_val: boolean) {
        console.log("setter has run");
    }
};
function foo() {
    console.log("right side evaluated");
    return true;
}
console.log("This one always runs the setter");
obj.prop = obj.prop || foo();
console.log("This one *sometimes* runs the setter");
obj.prop ||= foo();

Labeled Tuple Elements

Moving forward from this new version, tuple elements will now be labeled to improve the experience and encourage readability. This is very important because readability is key. Imagine a scenario like this using a tuple as a rest parameter:

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

This declaration should not be different from this one below:

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

for anytime foo is to be called.

foo("hello", 42); // works
foo("hello", 42, true); // error
foo("hello"); // error

We see how important readability can be with these. Even though modeling these up will not affect type-checking in any way, it becomes harder to use without labels. So the TypeScript team has now shipped labels for tuples.

type Range = [start: number, end: number];

To make the relationship between parameter lists and tuple types even deeper, the rest elements’ and optional elements’ syntax mirror the parameter lists’ syntax:

type Foo = [first: number, second?: string, ...rest: any[]];

One thing to remember if you use these labels is that all other elements inside the tuple must be labeled.

type Bar = [first: string, number];
//                         ~~~~~~
// error! Tuple members must all have names or all not have names.

It’s worth noting: Labels don’t require us to name our variables differently when destructuring. They’re purely there for documentation and tooling.

Another thing to note is that you do not have to name your variables differently when destructuring because they exist for documentation sake only.

function foo(x: [first: string, second: number]) {
    // ...
    // note: we didn't need to name these 'first' and 'second'
    let [a, b] = x;
    // ...
}

To learn more about labeled tuples, see the PR in the link here.

Class Property Inference from Constructors

In this new version, when you enable noImplicitAny, TypeScript now uses control flow analysis to tell what type the properties in your classes are.

class Square {
    // Previously: implicit any!
    // Now: inferred to `number`!
    area;
    sideLength;
    constructor(sideLength: number) {
        this.sideLength = sideLength;
        this.area = sideLength ** 2;
    }
}

When not every constructor path is assigned to an instance, TypeScript will label it undefined.

class Square {
    sideLength;
    constructor(sideLength: number) {
        if (Math.random()) {
            this.sideLength = sideLength;
        }
    }
    get area() {
        return this.sideLength ** 2;
        //     ~~~~~~~~~~~~~~~
        // error! Object is possibly 'undefined'.
    }
}

In some cases like when you have an initialize method of some sort, you will have to provide an explicit type annotation and a definite assignment assertion (!) if you’re in strictPropertyInitialization.

class Square {
    // definite assignment assertion
    //        v
    sideLength!: number;
    //         ^^^^^^^^
    // type annotation
    constructor(sideLength: number) {
        this.initialize(sideLength)
    }
    initialize(sideLength: number) {
        this.sideLength = sideLength;
    }
    get area() {
        return this.sideLength ** 2;
    }
}

Custom JSX Factories

In JSX, a fragment is an element that is used as an enclosing tag to allow you return more than one child element. This has been implemented in TypeScript a while back, but after the team at TypeScript saw how massively it was being adopted, they made it even easier to use. This new TypeScript version allows developers to customize the fragment factory through the jsxFragmentFactory option now available.

See this example. The tsconfig.json file below tells TypeScript to transform JSX in a way compatible with React, but switches each factory invocation to h instead of React.createElement, and uses Fragment instead of React.Fragment.

{
  "compilerOptions": {
    "target": "esnext",
    "module": "commonjs",
    "jsx": "react",
    "jsxFactory": "h",
    "jsxFragmentFactory": "Fragment"
  }
}

There may be cases where you need to have a different JSX factory on each file, you can now make use of the new /** @jsxFrag */ pragma comment. Like so:

// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from "preact";
let stuff = <>
    <div>Hello</div>
</>;

This gets changed to the JavaScript output below:

// Note: these pragma comments need to be written
// with a JSDoc-style multiline syntax to take effect.
/** @jsx h */
/** @jsxFrag Fragment */
import { h, Fragment } from "preact";
let stuff = h(Fragment, null,
    h("div", null, "Hello"));

Speed Improvements in build Mode

Before now, compiling TS code after a previous compile had been done with errors under the incremental flag would be extremely slow when you used the noEmitOnError flag. This is normally due to no caching being done for the previous compile session. This changes in this new version of TypeScript as these scenarios now have a speed boost in build mode.

This new version also allows you use the noEmit flag when you want to do incremental compilation. This was not previously allowed; however, it is now, as speed has become a priority in this case.

Conclusion

This is a collection of features I am excited about in the new TypeScript version 4.0. The next iteration is already in the works for version 4.1 and you can see the progress here. It is important to point out how much work in the version was done by community member contributions. The TypeScript community is one of the best I have seen and is constantly growing. What is your favorite new feature?


5E9A474B-EBDB-439E-8A4A-3B041B0BEDA6
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

Comments are disabled in preview mode.