Great New Features Released with TypeScript 3.4

Spread the love

TypeScript is improving every day. We keep getting new features with every release. In this article, we’ll look at the new stuff that’s released with TypeScript 3.4.

New features include better type inference for higher-order generic functions. changes to readonly types and faster builds with the --increment flag, and more.

New Features in TypeScript 3.4

–incremental Flag

To speed up builds after the first build, the --incremental flag of the TypeScript compiler will let us build only based on what’s changed.

We can add the option to tsconfig.json of our project to get this feature, under the compilerOptions section, as follows:

    "compilerOptions": {
        "incremental": true,
        "outDir": "./lib"
    "include": ["./src"]

It works by looking for the .tsbuildinfo which is created with the first build. If it doesn’t exist, then it’ll be generated. It’ll use this file to know what has been built and what has not.

It can be safely deleted and not impact our build. We can name the file with a different name by adding a tsBuildInfoFile option to the compilerOptions section tsconfig.json as follows:

    "compilerOptions": {
        "incremental": true,
        "tsBuildInfoFile": "./front-end-app",
        "outDir": "./lib"
    "include": ["./src"]

For composite projects, which has composite flag set to true in tsconfig.json, references between different projects can also be built incrementally. These projects will always generate a .tsbuildinfo files.

When the outFile option is used, then the build information file name will be based on the output file’s name. For example, if the output file is foo.js then the build information file will be foo.tsbuildinfo.

Higher-Order Type Inference in Generic Functions

When we have functions that take other functions as parameters, we’ll get type inference for the types of functions that are passed in and returned.

For example, if we have a function that composes multiple functions to return a new function as follows:

function compose<A, B, C, D>(
    f: (arg: A) => B,
    g: (arg: B) => C,
    h: (arg: C) => D
): (arg: A) => D {
    return x => h(g(f(x)));

When we fill in the types for the generic markers as follows:

function compose<A, B, C, D>(
    f: (arg: A) => B,
    g: (arg: B) => C,
    h: (arg: C) => D
): (arg: A) => D {
    return x => h(g(f(x)));

interface Employee {
    name: string;

const getName = (employee) =>;
const splitString = (name) => name.split('');
const getLength = (name) => name.length;

const fn = compose(getName, splitString, getLength)

Then we can call the fn function by writing:

const len: number = fn(<Employee>{ name: 'Joe' });

TypeScript 3.4 or later is smart enough to go through the chain of function calls and infer the types of each function automatically and the return type of the function returned from compose.

It can infer that fn returns a number.

TypeScript versions older than 3.4 will infer the empty object type and we get errors with the assignment expression above.

ReadonlyArray and readonly tuples

Using read-only array types is now easier with TypeScript 3.4. We can now declare a read-only array with the readonly keyword.

For example, if we want a read-only string array, we can write:

const strArr: readonly string[] = ['a', 'b', 'c'];

Now we have an array that we can’t push to, change entries or anything else that modifies the array.

This is much more compact compared to the ReadOnlyArray<string> type.

With TypeScript 3.4, we have the new read-only tuple type. We can declare a read-only tuple as follows:

const strTuple: readonly [string, string] = ['foo', 'bar'];

The readonly modifier on mapped types will convert to array-like types to their corresponding readonly counterparts.

For example, if we have the type:

type ReadOnly<T> = {
    readonly [K in keyof T]: T[K]

Then when we pass in a type into the generic type placeholder of Readonly as follows:

type foo = Readonly<{foo: number, bar: string}>;

We get that the foo type is:

type foo = {
    readonly foo: number;
    readonly bar: string;

As we can see, both fields have become readonly , which isn’t the case before TypeScript 3.4.

We can also use mapped types to remove the readonly modifier from all the fields. To do this, we add a - before the readonly modifier.

For example, we can write:

type Writable<T> = {
    -readonly [K in keyof T]: T[K]

interface Foo{
    readonly foo: string;
    readonly bar: number;

type foo = Writable<Foo>;

Then we get:

type WriteFoo = {
    foo: string;
    bar: number;

For the type foo .

The readonly modifier can only be used for syntax on array types and tuple types. It can’t be used on anything else.

Photo by Erin Wilson on Unsplash

Const Assertions

A constructor called const assertions is introduced with TypeScript 3.4. When we use it, we signal that literal types can’t change to a type that’s wider in scope, like going from 1 to string . Objects literals get readonly properties. Array literals become readonly tuples.

For example, the following is valid:

let x: 'foo' = "foo" as const;

We get that x is type 'foo' when we inspect its type.

Another example would be a number array:

let x = [1, 2] as const;

When we hover over x , we get that the type is readonly [1, 2] .


With TypeScript 3.4, we have multiple changes for read-only types, including using the readonly keyword to declare read-only arrays and tuples.

Also, we can add and remove the readonly modifier with mapped types with the readonly and -readonly modifiers before the index signature or field name.

The const assertion is for converting a value into a read-only entity.

High order generic functions that let us compose multiple functions together to return a new composed function also have smarter type inference than in earlier versions.

Finally, we have the --incremental flag to create incremental builds, which makes code build faster on subsequent builds.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *