Nearly two years after the release of TypeScript 2.0 and two months from the latest version 2.9, Microsoft has released the newest version of TypeScript. Let’s see what changes have been made and how they can affect our daily work.

In TypeScript 3.0 we can identify just five changes affecting the way you can use the language. Only one of those is flagged as a breaking change. But that’s enough introduction, let’s see what’s new!

Richer tuple types

Tuples in TypeScript are in fact JavaScript strongly-typed arrays (at the single element level) with constant length. At least, that’s the way it used to be, and you can see an example in Listing 1.

Listing 1 Tuples in TypeScript

Starting from TypeScript 3.0 we can define tuples with an undefined maximal number of elements, needing just a minimum. An example is given in Listing 2.

Listing 2 Tuple with the undefined maximal number of elements

The first element has a string type, which is mandatory, but the rest, which is just numbers, can be omitted. What’s more, we can even define the tuple without a lower limit (in fact it’s just an array) or an empty tuple. See Listing 3.

Listing 3 Empty tuple and tuple without lower limit

Such tuples have practical uses. For example, we can force an array to have at least one element on the types level. Another use, in a more complex context is shown in the next part of the article.

Extracting and spreading function arguments with tuple types

As you may know, in JavaScript we can use arguments provided to the function, even when we haven’t named any in the code, by using the arguments variable. Of course, this isn’t allowed in TypeScript (with default configuration), where we have to tell the compiler everything. The difference is shown in Listing 4.

Listing 4 Example of using arguments which aren’t defined in function’s definition

This is fine for the compiler, but when we’re using TypeScript, we don’t want to use the any type. Let’s say we want to have only strings and numbers provided. Then, we can of course change any to (string | number). But, this way we are telling thecompiler “I want to have an array which has strings or numbers”. We don’t restrict which element is a string, which is a number, and how many we should have. Let’s say we know that the first argument is a string, the following two are numbers and the rest are strings. From TypeScript 3.0 we can define it using tuple types as shown in Listing 5.

Listing 5 Implicitly defined types for each argument in an arguments array

How about a different use case? Let’s see Listing 6.

Listing 6 Function for composing two given functions and it’s usage

TypeScript 3.0 allows us to do a generic type T1 extends any[] which in fact creates a tuple type. Here we tell the compiler that T1 is the type of all of the first function’s arguments. It can be anything, but we will have it strongly typed. As you can see in the last line of listing 6, strict type checking works and tells us, that we can’t use a string in place of a number. This example shows, that we will be able to use such typings in functional programming. Maybe we will better typings for libraries like Ramda in the future?

“unknown”

As you may know, TypeScript has an any type which can handle literally everything — it reverts JavaScript’s weak typing for the specific variable. It’s a bad practice to overuse it, but due to JavaScript’s nature sometimes we need it. One case, we want to use any is when we don’t know the type, but we will check what it is and later use it strongly typed. With any we wouldn’t even need to check the type — we could use a variable just like that without compiler errors. From TypeScript 3.0 we can use the unknown type. It tells the compiler “We don’t know what we have here, so we can’t use it until we know what it is”. The difference is shown in Listing 7.

Listing 7 Differences between any and unknown

It’s worth noting that this is a breaking change, because from version 3.0 unknown has become a language’s keyword, so it can’t be used as a name anymore.

Support for defaultProps

Another novelty in TypeScript 3.0 is language-level support for React’s defaultProps. For those of you who haven’t heard of defaultProps, I’ll describe them briefly. Normally, in JavaScript (ECMAScript 6), if we would like to provide the default React component’s parameters, we would do it using static object defaultProps as shown in Listing 8.

Listing 8 Using defaultProps in JavaScript

As we can see, thanks to the usage of defaultProps we don’t need to perform null checks on title, because we are sure that it always has some value. In the past, TypeScript couldn’t understand this and we had to do a null check. In Listings 9 and 10 I’ve shown how we could approach this issue in TypeScript 2.x, and how we can do it now.

Listing 9 TypeScript 2.x way of using defaultProps

Listing 10 TypeScript 3.0 way of using defaultProps

As you can see, the code from the newest TypeScript version doesn’t need the null check, because the compiler can see the parameter value defined in defaultProps.

Project references

Probably the biggest new feature of TypeScript 3.0. From now on, we will be able to define cross‑project references. Thanks to this change, we will be able to use some new project architecture scenarios like:

· Using shared code for client and server projects. Compiled output would also share the code, not have a separate copy of each shared code file.

· Unit tests which don’t contain own copy of the main project’s files.

· Monorepos (many projects dependent on each other), along with the use of battle-tested solutions like Lerna and Yarn Workspaces

That’s all thanks to new entries in tsconfig: composite flag for compiler options and references. Also, we have a new parameter for tsc (TypeScript’s compiler): — build which can build whole TypeScript project’sin accordance with a given tsconfig.

As an example, let’s consider the following project structure:

The client’s project tsconfig.json is provided in Listing 11. It can be built using the command: tsc -b composite/client. To use a shared project we simply imported the things we need from it without any additional structures in the code.

Listing 11 tsconfig.json with composite flag and reference on shared project

After compiling both client and server we get the following structure:

As you can see, the “shared” directory is in fact shared between the client and the server. In older versions, we would get the “shared” directory copied to both client and server, thus creating the output structure different from the source structure.

For information on how to use project references with Lerna, I recommend this GitHub repository: https://github.com/RyanCavanaugh/learn-a.

Concluding…

As we have seen, there weren’t a lot of changes. Mostly they concerned a typing system to provide static types for many more JavaScript cases. The biggest change is, of course, project references. However, for now, we can’t tell how widely it will be used. I’m eager to see how much value it will provide to my new and existing projects and how it will affect other projects.

Post has also been published on Synergy Codes’ blog.