Notes on TypeScript 5.0 beta

TypeScript developers are a lucky bunch: for us, Christmas comes four times a year when the TypeScript team releases a new beta version. This is our opportunity to try out the latest features. TypeScript 5.0 beta came out on January 26, 2023. Let's take a look at what's new!

Why not TypeScript 4.10?

First, a note on version numbers. With semantic versioning, a change in the major version typically means breaking changes. And the number after 4.9 is 4.10, not 5.0. TypeScript doesn't really do semantic versioning. The whole point of new TypeScript releases is to find (existing) issues in your code. So in that sense, each release contains breaking changes.

Microsoft also counts in decimal. The version after 4.9 is 5.0, not 4.10. This causes some pain every tenth release since many packages on npm declare that they require typescript@4.x. This can either hold you back on an old version of TypeScript or lead to fragmentation, as I noticed with TypeScript 4.0 back in 2020:

Why doesn't TypeScript do versioning in the usual way? The definitive answer comes from TypeScript Tech Lead Ryan Cavanaugh:

The trade-off for getting millions of dollars of engineering investment in the TypeScript project is that marketing gets to control version numbers to a certain extent.

So there you go. Every 2.5 years we have to deal with this extra pain. At least marketing didn't decide that the version after 3.1 was 95!

New Errors

Every new version of TypeScript has the potential to surface new errors in your code. As long-time readers of this blog know, the code samples in Effective TypeScript are all type-checked. One of the benefits of this is that when new TS versions come out, I can type-check my book against them. Sometimes I learn that my book is out of date and sometimes I find new bugs in TypeScript.

TypeScript 5.0 didn't surface any new errors in Effective TypeScript. Huzzah! 🎉

There were a few new errors in my work project. All of them involved comparing string|number to number:

error TS2365: Operator '>=' cannot be applied to types 'string | number' and 'number'.

96 (val >= minVal) &&
~~~~~~~~~~~~~

The full code looks like this:

if (val >= minVal && val <= maxVal) {
return true;
}

Here val has type string | number while minVal and maxVal have type number. JavaScript is notoriously eager to coerce types so that operations make sense:

> 10 > "9"
true
> 10 > "x"
false

Not helpful, JS! TypeScript has always barred comparisons between strings and numbers. Now the noose has tightened just a bit more and you may have to be more explicit about conversions.

Performance

The TypeScript team claims a 10-20% speedup in build times with TypeScript 5.0 beta. I was able to confirm this both on my work project (Delve) and in running literate-ts against Effective TypeScript:

  • literate-ts / Effective TypeScript:
    • TS 4.9.5: 194.12s
    • TS 5.0-beta: 181.31s (6.6% speedup)
  • Delve:
    • TS 4.9.5: 46.36s
    • TS 5.0-beta: 38.27s (17.5% speedup)

Compiler speed is important and these are both welcome improvements! You can read the release notes for more details on how these speedups were achieved.

const type parameters

const type parameters are the one new language feature in this release. These are like as const but applied on the function declaration, rather than at the call site.

The applications I see for this in my own code are mostly small quality of life wins. For example, my work project has a DropdownList component that takes a list of options and a selected option. The list of options should be a tuple of string literals, and the selected option should be one of those literals:

function MyComponent() {
<DropdownList
dropdownOptions={['Option A', 'Option B', 'Option C'] as const}
selectedOption="Option A"
/>
}

With TypeScript 5.0 we can move the const into the declaration of DropdownList:

const DropdownList = <const DropdownOption extends string>(
props: {
options: readonly DropdownOption[];
selectedOption: DropdownOption;
}
) => {
// ...
}

And drop the as const at the callsite:

function MyComponent() {
<DropdownList
options={['Option A', 'Option B', 'Option C']}
selectedOption="Option A"
/>
}

If you inspect options here, you can see that its type is inferred as readonly ("Option A" | "Option B" | "Option C")[] whereas before it would have been inferred as readonly string[]. Nice! If you factor the list of options out into a variable, though, the context will be lost and you'll be back to using as const.

What else is this useful for? Back in 2020 I talked about the tuple helper function:

const tuple = <T extends unknown[]>(...args: T): T => args;

const p1 = [1, 2]; // type is number[]
const p1 = tuple(3, 4); // type is [number, number]

What if we use a const type parameter on tuple? Interestingly, it becomes a sort of "deep tuple":

export function tuple<const T extends readonly unknown[]>(...x: T) {
return x;
}

const coords1 = [[1, 2], [3, 4], [5, 6]];
// ^? number[][]

const coords2 = tuple([1, 2], [3, 4], [5, 6]);
// ^? readonly [readonly [1, 2], readonly [3, 4], readonly [5, 6]]

Is this useful? Maybe. There are always compelling applications of new language features and I'm sure I'm missing some here. Do you have a use for const type parameters? Let me know in the comments!

Enums are unions

In Effective TypeScript Item 53 ("Prefer ECMAScript Features to TypeScript Features") I discourage using enums since they're not an ECMAScript feature and generally break the mold of how TypeScript relates to JavaScript ("JavaScript + Types").

With TypeScript 5.0, enums become a little more sane. Whereas this was OK in TypeScript 4.9:

enum E {
a = 1,
b = 2,
}
const e: E = 30; // ok

It's an error in TypeScript 5.0:

const e: E = 30;
// ~ Type '30' is not assignable to type 'E'. (2322)

So are enums OK now? As it turns out, the newfound sanity is only surface deep:

let n = 30;
const e: E = n; // ok :(

TypeScript enums are complicated and problematic. While they become slightly saner with TypeScript 5.0, I still say "avoid them."

Conclusions

There are more changes in the new release than what I've written about here. Check out the release notes for full details. With any luck, a release candidate (RC) will arrive on February 28th and the final cut of TypeScript 5.0 will be out on March 14th.

Like this post? Consider subscribing to my newsletter, the RSS feed, or following me on Twitter.
Effective TypeScript Book Cover

Effective TypeScript shows you not just how to use TypeScript but how to use it well. Now in its second edition, the book's 83 items help you build mental models of how TypeScript and its ecosystem work, make you aware of pitfalls and traps to avoid, and guide you toward using TypeScript’s many capabilities in the most effective ways possible. Regardless of your level of TypeScript experience, you can learn something from this book.

After reading Effective TypeScript, your relationship with the type system will be the most productive it's ever been! Learn more »