Skip to main content

On mobile? Send a link to your computer to download HTTP Toolkit there:

No spam, no newsletters - just a quick & easy download link

On mobile? Send a link to your computer to download HTTP Toolkit there:

No spam, no newsletters - just a quick & easy download link

typescript

javascript

What's coming in TypeScript 4?

TypeScript 4 is coming up fast: a first beta release is planned for this week (June 25th), with the final release aiming for mid-August.

It's important to note that TypeScript does not follow semveropens in a new tab, so 4.0 is not as big a deal as it sounds! There can be (and often are) breaking changes between any minor TypeScript versions, and major version bumps like this happen primarily for marketing reasons, not technical ones.

This bump to 4.0 doesn't suggest that everything is going to break, and this won't be a huge world-changing release, but it does bring some nice additions, particularly on the typing side. For projects like HTTP Toolkit (written entirely in TypeScript) that means faster development & fewer bugs!

Let's dive into the details:

Variadic tuple types

Also known as 'variadic kindsopens in a new tab', this is a complex but substantial new feature for TypeScript's type system.

~~It's not 100% confirmed yet (the PRopens in a new tab remains unmerged!), but it's explicitly in the 4.0 roadmap, and Anders Hejlsberg himself has called it outopens in a new tab as planned for the coming release.~~ Update: PR now merged, looks like this is happening!

Explaining this is complicated if you don't have an strong existing grasp of type theory, but it's easy to demo. Let's try to type a concat function with tuple arguments:

Code example

Code examplefunction concat(
    nums: number[],
    strs: string[]
): (string | number)[] {
    return [...nums, ...strs];
}

let vals = concat([1, 2], ["hi"]);
let val = vals[1]; // infers string | number, but we *know* it's a number (2)

// TS does support accurate types for these values though:
let typedVals = concat([1, 2], ["hi"]) as [number, number, string];
let typedVal = typedVals[1] // => infers number, correctly

This is valid TypeScript code today, but it's suboptimal.

Here, concat works OK, but we're losing information in the types and we have to manually fix that later if we want to get accurate values elsewhere. Right now it's impossible to fully type such a function to avoid this.

With variadic types though, we can:

Code example

Code examplefunction concat<N extends number[], S extends string[]>(
    nums: [...N],
    strs: [...S]
): [...N, ...S] {
    return [...nums, ...strs];
}

let vals = concat([1, 2], ["hi"]);
let val = vals[1]; // => infers number
const val2 = vals[1]; // => infers 2, not just any number

// Go even further and accurately concat _anything_:
function concat<T extends unknown[], U extends unknown[]>(
    t: [...T],
    u: [...U]
): [...T, ...U] {
    return [...t, ...u];
}

In essence, tuple types can now include ...T as a generic placeholder for multiple types in the tuple. You can describe an unknown tuple ([...T]), or use these to describe partially known tuples ([string, ...T, boolean, ...U]).

TypeScript can infer types for these placeholders for you later, so you can describe only the overall shape of the tuple, and write code using that, without depending on the specific details.

This is neat, and applies more generally than just concatenating arrays. By combining this with existing varadic functions, like f<T extends unknown[]>(...args: [...T]), you can treat function arguments as arrays, and describe functions with far more flexible argument formats and patterns than in the past.

For example, right now rest/varadic parameters in TypeScript must always be the last param in a function. For example, f(a: number, ...b: string[], c: boolean) is invalid.

With this change, by defining the arguments of the function using a variadic tuple type like f<T extends string[]>(...args: [number, ...T, boolean]) you can do that.

That's all a bit abstract. In practice, this means you'll be able to:

  • Destructure array types:
    type head = <H extends unknown, T extends unknown[]>(list: [H, ...T]) => H
  • Do many of the things allowed by mapped typesopens in a new tab, but on arbitrary-length arrays of values, not just on objects.
  • Infer full types for functions with variadic arguments: type f = <T extends unknown[]>(...args: [...T]) => T
  • Infer proper types even for extra complicated partially-known variadic arguments: type f = <T extends unknown[]>(...args: [string, ...T, boolean]) => T
  • Fully define typesopens in a new tab for promisify.
  • Create accurate types for many other higher-order function definitions, like curry, apply, compose, cons, ...
  • Kill all sorts of workaroundsopens in a new tab where you had to separately define an overload for each possible number of arguments (I've been guiltyopens in a new tab of this myself).

Even if you're not writing a lot of higher order functions, improved typing here should allow more detailed types to spread far and wide through your code, inferring away many non-specific array types, and improving other types all over the place.

There's a lot more depth and many other use cases for this - take a look at the full GitHub discussionopens in a new tab for more info.

Labelled Tuples

As a related but drastically simpler feature: TypeScript will allow labelling the elements of your tuples.

What does the below tell you?

Code example

Code examplefunction getSize(): [number, number];

How about now?

Code example

Code examplefunction getSize(): [min: number, max: number];

These labels disappear at runtime and don't do any extra type checking, but they do make usage of tuples like these far clearer.

These also work for rest & optional arguments too:

Code example

Code exampletype MyTuple = [a: number, b?: number, ...c: number[]];

For more info, checkout the GitHub issueopens in a new tab.

Property type inference from constructor usage

A nice clear improvement to type inference:

Code example

Code exampleclass X {

    private a;

    constructor(param: boolean) {
        if (param) {
            this.a = 123;
        } else {
            this.a = false;
        }
    }

}

In the above code right now, the type of a is any (triggering an error if noImplicitAny is enabled). Property types are only inferred from direct initialization, so you always need either an initializer or an explicit type definition.

In TypeScript 4.0, the type of a will be number | boolean: constructor usage is used to infer property types automatically.

If that's not sufficient, you can still explicitly define types for properties, and those will be used in preference when they exist.

Short-circuit assignment operators

Not interested in typing improvements? TypeScript 4.0 will also implement the stage 3 JS logical assignmentopens in a new tab proposal, supporting the new syntax and compiling it back to make that usable in older environments too.

That looks like this:

Code example

Code examplea ||= b
// equivalent to: a = a || b

a &&= b
// equivalent to: a = a && b

a ??= b
// equivalent to: a = a ?? b

Nowadays the last option is probably the most useful here, unless you're exclusively handling booleans. This null-coalescing assignment is perfect for default or fallback values, where a might not have a value.

The also rans

That's a few of the bigger notices, but there's a lot of other good stuff here too:

None of these are individually huge, but nonetheless, cumulatively it'll improve life for TypeScript developers, with some great improvements to type safety and developer experience all round.

I should note that none of this is final yet! I've skipped a few discussed-but-not-implemented changes - from awaited Topens in a new tab to placeholder typesopens in a new tab - and it's quite possible some of these features could suddenly appear in the next month, or equally that a new problem could cause changes in the implemented features above, so do keep your eyes peeled...

Hope that's useful! Get in touch on Twitteropens in a new tab if you've got questions or comments.

Share this post:

Blog newsletter

Become an HTTP & debugging expert, by subscribing to receive new posts like these emailed straight to your inbox:

Related content

node.js

Automatic npm publishing, with GitHub Actions & npm granular tokens

This week, at long last, GitHub announced granular access tokens for npm. This is a big deal! It's great for security generally, but also particularly useful if you maintain any npm packages, as it removes the main downside of automating npm publishing, by allowing you to give CI jobs only a very limited token instead of full 2FA-free access to your account. In the past, I've wished for this, because I maintain a fair few npm packages including some very widely used ones. The previous solution of "just disable 2FA on your account, create an all-powerful access token with global access to every package, and give that token to your CI job" was not a comfortable one.

decentralized-web

Debugging WebRTC, IPFS & Ethereum with HTTP Toolkit

HTTP is important on the web, but as other alternative protocols grow popular in networked applications, it's often important to be able to capture, debug and mock those too. I've been working on expanding HTTP Toolkit's support for this over the past year (as one part of a project funded by EU Horizon's Next Generation Internet initiative), to extend HTTP Toolkit to cover three additional rising protocols that are often used alongside simple HTTP in decentralized web applications: WebRTC, IPFS & Ethereum.

decentralized-web

Testing libraries for the Decentralized Web

The world of decentralized web applications is an exciting place that has exploded in recent years, with technologies such as IPFS and Ethereum opening up possibilities for a peer-to-peer web - creating applications that live outside the traditional client/server model, where users to interact and control their own data directly. At the same time, it's still immature, and for software developers it lacks a lot of the affordances & ecosystem of the traditional HTTP-based web app world. There's far fewer tools and libraries for developers working in this space.