5 min read
TypeScript 7.1 Beta: stable es2026 lib and what to check
TypeScript 7.1 Beta makes es2026 stable: Map upserts, Uint8Array base64, Error.isError, plus typed import attributes and one breaking change.

TypeScript 7.1 Beta landed on October 20, and the headline for most apps is not the compiler speedup: it is that --target es2026 and --lib es2026 are now stable. This post walks through the new library types, the import features that came with the beta, and the one breaking change you should check before upgrading. I ran every snippet below with typescript@7.1.0-beta and in Chromium.
// tsconfig: "target": "es2026", "lib": ["es2026"]
const cache = new Map<string, string[]>();
function add(key: string, value: string) {
cache
.getOrInsertComputed(key, () => [])
.push(value);
}
add("x", "1");
add("x", "2");
console.log(cache.get("x")); // ["1", "2"]
Trying the beta
The 7.1 Beta announcement says to install it with the beta tag:
npm install -D typescript@beta
npx tsc -v # Version 7.1.0-beta
The team expects a release candidate about a month after the beta, then a final release shortly after. Treat this as something to try on a branch, not to ship.
What es2026 adds to the lib
According to the announcement, the stable es2026 target and lib include these:
MapandWeakMap:getOrInsert()andgetOrInsertComputed()Uint8Array:toBase64(),toHex(),fromBase64(),fromHex(),setFromBase64(),setFromHex()Error.isError(),Array.fromAsync(),Math.sumPrecise(),Iterator.concat(),JSON.rawJSON()
These are type declarations only. TypeScript does not polyfill anything, so the type check passing says nothing about your runtime. More on that below.
Map upsert without the get/set dance
The classic "group by key" code does a get, a null check, a set, and then another get. getOrInsertComputed collapses that, and it only calls your factory when the key is missing:
const groups = new Map<string, number[]>();
for (const [k, v] of [["a", 1], ["b", 2], ["a", 3]] as const) {
groups.getOrInsertComputed(k, () => []).push(v);
}
// Map { "a" => [1, 3], "b" => [2] }
Use getOrInsert(key, value) when the default is a cheap constant. Use the Computed form when building the default is expensive or allocates, such as a new array or object. The MDN page for Map.prototype.getOrInsertComputed has the details.
Base64 and hex on Uint8Array
No more btoa(String.fromCharCode(...bytes)), which also breaks on large arrays because of the argument spread:
const bytes = new Uint8Array([72, 105, 33]);
bytes.toBase64(); // "SGkh"
bytes.toHex(); // "486921"
bytes.toBase64({ alphabet: "base64url", omitPadding: true });
Uint8Array.fromBase64("SGkh"); // Uint8Array [72, 105, 33]
Uint8Array.fromHex("486921"); // Uint8Array [72, 105, 33]
In my Chromium run, the base64 and hex values matched the comments above exactly. The base64url option type-checks, which is handy for JWT-style payloads.
Error.isError for cross-realm errors
instanceof Error fails for errors created in another realm, such as an iframe. Error.isError does not. I checked this in Chromium by creating an error from an iframe's Error constructor:
Error.isError(new TypeError("x")); // true
Error.isError({ message: "x" }); // false
// error created by an iframe's Error constructor:
// Error.isError(err) -> true
// err instanceof Error -> false
This matters for code that handles errors coming from other realms, such as iframes.
The rest
Array.fromAsync(gen()) resolved to [1, 2] for an async generator, Iterator.concat([1, 2], new Set([3])) yielded 1, 2, 3, and Math.sumPrecise([0.1, 0.2, 0.3]) returned 0.6 where plain addition gives 0.6000000000000001.
The runtime is your problem
Here is the part that bites. With lib: ["es2025"], the same file fails with errors like TS2550: Property 'getOrInsertComputed' does not exist on type 'Map<string, string[]>'. Switching to es2026 makes the errors go away, but only the types change.
I checked the actual runtimes:
- Chromium 153 (Playwright): all of the APIs above were defined.
- Node 22.23.4:
Array.fromAsyncandJSON.rawJSONexist, butError.isErrorandUint8Array.fromBase64areundefined.
So the rule is the same as always: pick your lib to match the oldest runtime you support, or polyfill. Check MDN's compatibility tables for each API before you flip the setting in a shared config.
Wildcard module declarations with import attributes
The beta also lets an ambient module declaration require a specific import attribute. That gives you typed imports for things like CSS, text, or bytes without a global declare module "*.md" that matches by file extension:
// text.d.ts
declare module "*" with { type: "text" } {
const content: string;
export default content;
}
import notes from "./notes.md" with { type: "text" };
const len: number = notes.length; // ok
const n: number = notes; // error TS2322: string is not assignable to number
I confirmed both lines: the first compiles, the second produces TS2322. Per the announcement, equivalent forms exist for type: "css" (a CSSStyleSheet) and type: "bytes" (a Uint8Array), and matching imports are not checked against disk. Declare these in your app, not in a published library.
The beta also adds source phase imports (import source mod from "./x.wasm"), which need --module set to esnext, nodenext or preserve. I did not test those, so check the announcement before relying on them.
Breaking change: distributive conditional types
The announcement calls out stricter handling inside distributed branches of conditional types. The type parameter there is now treated as a narrower, separate type parameter. Code that passed it where a constraint on the original parameter was required can now error. If your type-level utilities start failing after the upgrade, this is the first place to look. The suggested fixes are to remove an unnecessary constraint or to add extra conditional checks.
The generated DOM types also changed, so expect some noise in DOM-heavy code (tracked in microsoft/TypeScript#63702).
A config reminder from 6.0
If you are coming from older setups, the 6.0 line changed defaults you will hit in CI. Per the TypeScript 6.0 announcement, running tsc foo.ts while a tsconfig.json exists is now an error (TS5112) unless you pass --ignoreConfig. I hit exactly that error while testing, so update any scripts that compile single files.
Takeaway
- Try
typescript@betaon a branch:es2026is stable, and it bringsMapupserts,Uint8Arraybase64/hex,Error.isErrorand more as types. - Types are not polyfills. Node 22.23.4 lacked
Error.isErrorandUint8Array.fromBase64in my test, so matchlibto your oldest runtime. declare module "*" with { type: "text" }gives you attribute-based typing for non-JS imports.- Watch for new errors in conditional-type utilities and DOM-heavy code.
- Wait for the RC and final release before shipping it to production.

