ECMAScript 2026 features are now landing in tools and runtimes. TypeScript 7.1 Beta adds es2026 as a stable --target and --lib option, and its release candidate is due soon. On the runtime side, the Node.js 27 Alpha upgrade to V8 15.6 turns on Math.sumPrecise() and several new iterator helpers by default.

This post is a practical tour of the new built-in APIs that TypeScript now types under lib: es2026. For each one you’ll see the problem it solves and how to use it. Runtime support still varies, so there’s a feature-detection pattern at the end.

Enable ES2026 types in TypeScript

Install the beta and set your tsconfig.json:

npm install -D typescript@beta
{
  "compilerOptions": {
    "target": "es2026",
    "lib": ["es2026", "dom"],
    "module": "nodenext"
  }
}

According to the TypeScript 7.1 Beta announcement, the es2026 lib adds or moves declarations for Math.sumPrecise(), Iterator.concat(), JSON.rawJSON(), Error.isError(), Map/WeakMap getOrInsert() and getOrInsertComputed(), the Uint8Array base64/hex methods, and Array.fromAsync().

1. Math.sumPrecise(): sums without float drift

Adding decimals in a loop builds up floating-point error. Math.sumPrecise() takes an iterable and reduces that error:

[0.1, 0.2, 0.3].reduce((a, b) => a + b); // 0.6000000000000001
Math.sumPrecise([0.1, 0.2, 0.3]);         // 0.6

It’s ideal for totals in dashboards and reports. (For money, integer paise or cents are still the safest option.)

const cart = [{ price: 19.99 }, { price: 5.01 }, { price: 0.1 }];
const total = Math.sumPrecise(cart.map(item => item.price));

2. Map.getOrInsert() and getOrInsertComputed()

This is probably the pattern you write most often:

// Old way
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);

Now it’s one line:

const groups = new Map();
for (const order of orders) {
  groups.getOrInsert(order.customerId, []).push(order);
}

getOrInsert(key, value) returns the existing value, or stores and returns the default. Use getOrInsertComputed(key, fn) when the default is expensive to build, because the callback only runs when the key is missing:

const cache = new Map();
function getUser(id) {
  return cache.getOrInsertComputed(id, () => loadUserFromDb(id));
}

WeakMap gets the same two methods. That’s useful for per-object metadata.

3. Uint8Array base64 and hex, built in

No more btoa(String.fromCharCode(...bytes)) tricks, and no Buffer needed in the browser:

const bytes = new TextEncoder().encode("Hello, ES2026");

const b64 = bytes.toBase64();          // "SGVsbG8sIEVTMjAyNg=="
const hex = bytes.toHex();             // "48656c6c6f2c..."

const fromB64 = Uint8Array.fromBase64(b64);
const fromHex = Uint8Array.fromHex(hex);

console.log(new TextDecoder().decode(fromB64)); // "Hello, ES2026"

There are also setFromBase64() and setFromHex(), which decode into an existing array. That’s handy when you reuse buffers.

A common use is hashing with the Web Crypto API:

async function sha256Hex(text) {
  const data = new TextEncoder().encode(text);
  const digest = await crypto.subtle.digest("SHA-256", data);
  return new Uint8Array(digest).toHex();
}

4. Error.isError(): reliable error checks

err instanceof Error fails for errors created in another realm (an iframe, a vm context, some test runners). Error.isError() checks for a real error object, wherever it was created:

function toMessage(e) {
  return Error.isError(e) ? e.message : String(e);
}

It’s great in catch blocks of libraries, where you can’t control what gets thrown.

5. Iterator.concat() and Array.fromAsync()

Iterator.concat() chains several iterables lazily, without building a temporary array:

const all = Iterator.concat(adminIds.values(), userIds.values());
for (const id of all) notify(id);

Array.fromAsync() collects an async iterable into an array, which is useful with paginated APIs written as async generators:

async function* pages() {
  let url = "/api/items?page=1";
  while (url) {
    const res = await fetch(url).then(r => r.json());
    yield* res.items;
    url = res.next;
  }
}

const items = await Array.fromAsync(pages());

6. JSON.rawJSON(): exact big numbers

JSON.stringify() can’t output a number larger than Number.MAX_SAFE_INTEGER without losing precision. JSON.rawJSON() lets you insert raw JSON text:

const id = 12345678901234567890n;
JSON.stringify({ id: JSON.rawJSON(id.toString()) });
// '{"id":12345678901234567890}'

Coming next: esnext proposals in TypeScript 7.1

TypeScript 7.1 also types some proposals under lib: esnext, including Promise.allKeyed, Iterator.zip, Iterator.zipKeyed, and the iterator helpers chunks, join and includes. The Node.js 27 Alpha V8 upgrade already enables join, includes, zip and zipKeyed by default:

[1, 2, 3].values().map(x => x * 2).join("-"); // '2-4-6'
Iterator.zipKeyed({ id: [1, 2], name: ["a", "b"] }).toArray();
// [{ id: 1, name: 'a' }, { id: 2, name: 'b' }]

Before and after: refactoring a real function

Here’s a small reporting function written the “old” way, which you’ll find in many codebases:

function summarise(orders) {
  const byCustomer = new Map();
  for (const o of orders) {
    if (!byCustomer.has(o.customerId)) byCustomer.set(o.customerId, []);
    byCustomer.get(o.customerId).push(o.amount);
  }
  const result = {};
  for (const [id, amounts] of byCustomer) {
    result[id] = amounts.reduce((a, b) => a + b, 0);
  }
  return result;
}

And the same logic using ES2026 APIs:

function summarise(orders) {
  const byCustomer = new Map();
  for (const o of orders) {
    byCustomer.getOrInsert(o.customerId, []).push(o.amount);
  }
  return Object.fromEntries(
    byCustomer.entries().map(([id, amounts]) => [id, Math.sumPrecise(amounts)])
  );
}

It’s shorter, it doesn’t need a separate has() check, and the totals don’t pick up floating-point noise. The .entries().map(...) call uses the iterator helpers that shipped in ES2025, so no temporary array is created.

When you refactor like this, change one pattern at a time and keep your tests green. Small, mechanical changes are easy to review and easy to revert if a runtime turns out not to support a method yet.

Check support before you ship

TypeScript types don’t add runtime support. Before you use these APIs in production, check your target browsers and Node version, and feature-detect where needed:

const supports = {
  sumPrecise: typeof Math.sumPrecise === "function",
  getOrInsert: typeof Map.prototype.getOrInsert === "function",
  base64: typeof Uint8Array.prototype.toBase64 === "function",
  isError: typeof Error.isError === "function",
};
console.table(supports);

Where support is missing, use a polyfill (core-js covers many ECMAScript proposals) or keep the older pattern for now.

Takeaway

ES2026 is a quality-of-life release. getOrInsert() cleans up grouping code, sumPrecise() fixes a classic float annoyance, and native base64/hex removes a dependency from many front-ends. Turn on lib: es2026 in TypeScript, try the APIs in the latest runtimes, and adopt them gradually as support spreads.

Sources: Announcing TypeScript 7.1 Beta · Node.js: Notable V8 changes from Node.js 26 to 27 (Alpha)