Notes on TypeScript 5.1

Every three months we get a new TypeScript release and TypeScript 5.1 landed on June 1, 2023. This release has a few interesting new features, but by far the most noticeable changes are performance improvements and error message ergonomics. Let's take a look!

Performance Improvements

When you hear "new TypeScript version", the natural tendency is to think about new language features. But what would you be more excited about: an exciting new feature like template literal types or a 10% faster compiler? Sorry, you can't have both!

The TypeScript team takes compiler performance incredibly seriously and every single set of release notes includes a few performance optimizations. A recent theme has been improving build times for projects that use complex libraries like Material-UI. The 5.1 release includes several optimizations that add up to a big win.

As readers of this blog may recall, Effective TypeScript is itself type-checked using literate-ts. The idea is that when new versions of TypeScript come out, I can quickly check whether any of the hundreds of code samples in the book produce new and unexpected errors. This is a great confidence booster that my book still matches reality, but it also means that Effective TypeScript can serve as a good gauge of what's changed between releases.

First let's look at performance:

  • Checking Effective TypeScript (TS 5.0.4): 180.6s average
  • Checking Effective TypeScript (TS 5.1.3): 170.9s average

That's about a 5% speedup. Not bad!

Improved Error Messages

literate-ts is very sensitive to how error messages and types display. TypeScript 5.1 includes a nice change in how type errors on return statements are displayed that didn't make it into the release notes. In previous versions of TypeScript, if you returned an expression of the wrong type, you'd get the dreaded red squiggles under both the return keyword and the entire expression. For multiline expressions, this could be a lot of red!

Code sample showing lots of red

With TypeScript 5.1, the red squiggles only appear under the return keyword:

Code sample showing much less red

This is less distracting and makes it easier for you to inspect your code to find the source of the error. Not a huge change, but a nice win nonetheless. Next time you're debugging a type error in a return statement, thank Mateusz Burzyński for the change!

New Errors

Upgrading TypeScript often uncovers existing mistakes in your code and TS 5.1 was no exception. There was one new error that came up in a few of my projects.

TypeScript has long flagged duplicate keys in object literals:

const obj = {
foo: 12,
bar: 34,
foo: 56,
// ~~~ An object literal cannot have multiple properties with the same name.

But if you used computed keys, this error would go away:

const FOO = 'foo';
const BAR = 'bar';

const obj = {
[FOO]: 12,
[BAR]: 34,
[FOO]: 56, // (not an error in TS 5.0)

With TS 5.1, this is an error:

const obj = {
[FOO]: 12,
[BAR]: 34,
[FOO]: 56,
// ~~~~ An object literal cannot have multiple properties with the same name.

This check only occurs for single-valued literal types.

Notes on other changes

The "headline" features in the official release notes for TS 5.1 are mostly niche changes that won't affect many users immediately. Here's a quick rundown:

  • Easier Implicit Returns for undefined-Returning Functions I've never personally run across a function that was declared to return undefined rather than void. But if you work with such functions, your life gets easier with TS 5.1.
  • Unrelated Types for Getters and Setters This seems like a bad idea though as the CSS example in the release notes makes clear, this is an established pattern in the wild that TypeScript needs to model. I tend to avoid getters and setters (and classes in general). This relates to Effective TypeScript's Item 29: Be Liberal in What You Accept and Strict in What You Produce.
  • Decoupled Type-Checking Between JSX Elements and JSX Tag Types This seems quite in the weeds, but the gist is that it's future-proofing for async React components, which may land sometime in the future. When these eventually land, we'll be happy that TypeScript supports them out of the box.

I was extremely excited about one other change in the TypeScript 5.1 RC that sadly got cut for the release: a "move to existing file" refactor. There's a nice video of this in action in the RC release notes. This has been a long-standing and much-upvoted feature request. You can move a symbol to a new file, but not to an existing file.

I have a workaround, but it's a bit gross. Say you want to move a symbol to an existing file src/utils/my-utils.ts. Instead, move it to a new file src/utils/new-file.ts. This will update all the imports for the symbol to point to the new file. Then cut/paste the definition of your symbol into my-utils.ts, delete new-file.ts and run a big Find/Replace to update all the imports:

git ls-files | xargs perl -i -pe 's,new-file.ts,my-utils.ts,`

Like I said, gross! Hopefully this feature lands more permanently in TypeScript 5.2.


TypeScript 5.1 is not a major release when it comes to new language features, but it does include some performance wins, it may catch some new errors in your project, and it lays the groundwork for more changes in the future. Keep your eyes out for the TypeScript 5.2 beta which should be landing any day now and looks to have some exciting new features.

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. The book's 62 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 »