TC39 Updates: Error Cause, Temporal, Array.findLast & Module Fragments
Amid the pandemic‑driven shift to fully online TC39 meetings, the committee accelerated meeting frequency while trimming agendas, advancing proposals such as Error Cause to Stage 3, introducing the Temporal API to replace Date, adding Array.findLast methods, and exploring Module Fragments to improve JavaScript module handling.
Because of the pandemic, TC39 meetings moved from a hybrid format to fully online, making time‑zone differences a major challenge. Starting this year, the committee shortened each meeting but increased their frequency to alleviate the time‑zone issue, resulting in fewer agenda items and fewer progressing proposals.
Stage 2 → Stage 3
A proposal moves from Stage 2 to Stage 3 when it meets two criteria:
All proposal text is written, reviewed, and signed off by the designated TC39 members.
The ECMAScript editors have signed off.
Error Cause
The Alibaba representative’s proposal Error Cause reached Stage 3 and will start being implemented in JavaScript engines, with experimental releases in browsers and Node.js.
Proposal link: https://github.com/tc39/proposal-error-cause
The proposal adds an optional options parameter to the Error constructor, allowing a cause property that can hold any JavaScript value (e.g., undefined or a string). This value is assigned to error.cause.
Similar “cause” features exist in other languages such as C# Exception Cause, Java Exception Cause, and Python’s raise … from. In the JavaScript ecosystem, libraries like verror and @netflix/nerror already use this pattern extensively.
With this proposal, developer tools like Chrome DevTools can automatically display the cause property, improving the debugging experience.
try {
return await fetch('//unintelligible-url-a') // throws a low‑level error
.catch(err => {
throw new Error('Download raw resource failed', { cause: err }) // wrap low‑level error
})
} catch (err) {
console.log(err)
console.log('Caused by', err.cause)
// Error: Download raw resource failed
// Caused by TypeError: Failed to fetch
}Temporal
The original Date API in JavaScript suffers from many design flaws: limited time‑zone support, unreliable string parsing, mutable objects, unpredictable daylight‑saving behavior, impractical calculation API, and lack of support for non‑Gregorian calendars.
The Temporal proposal introduces a new global namespace Temporal with modern date‑time APIs designed to address these issues.
Easy‑to‑use date and time calculation APIs.
Native time‑zone support, including daylight‑saving handling.
All Temporal objects are immutable.
Strict date‑string parsing.
Support for multiple calendars (Gregorian, lunar, Islamic, etc.).
Temporal provides specialized types (e.g., PlainYearMonth, PlainMonthDay) that allow precise representation of partial dates and avoid automatic zero‑filling.
Each Temporal type has a string representation suitable for storage and transmission, and extensions to ISO‑8601/RFC‑3339 are being standardized.
Date value calculation
With Date, developers must manually read, compute, and set values, leading to errors in leap‑year scenarios:
const now = new Date('2020-02-29T12:00:00.000Z');
now.setFullYear(now.getFullYear() + 1);
// ❌ Result: 2021-03-01T12:00:00.000Z (expected 2021-02-28)Temporal provides built‑in calculation methods:
const now = Temporal.PlainDateTime.from('2020-02-29T12:00:00.000Z');
const after = now.add({ years: 1 });
// ✅ Result: 2021-02-28T12:00:00.000ZStrict string parsing
new Date('2019-02-29T12:00:00.000Z')silently normalizes the invalid date, whereas Temporal throws a RangeError:
Temporal.PlainDateTime.from('2019-02-29T12:00:00.000Z');
// ✅ Throws RangeErrorLeap‑year calculation
Temporal’s Calendar object correctly determines leap years across calendars:
const calendar = Temporal.Calendar.from('iso8601');
const isLeap = year => calendar.inLeapYear(Temporal.PlainYearMonth.from({ year, month: 2 }));
isLeap(1900); // falseArray find from last
The proposal adds Array.prototype.findLast and Array.prototype.findLastIndex, which search from the end of the array, analogous to Array.prototype.find but reversed.
Proposal link: https://github.com/tc39/proposal-array-find-from-last
const array = [{value:1},{value:2},{value:3},{value:4}];
array.findLast(n => n.value % 2 === 1); // → {value:3}
array.findLastIndex(n => n.value % 2 === 1); // → 2
array.findLastIndex(n => n.value === 42); // → -1Module Fragments
This proposal allows a single JavaScript file to contain multiple named modules, improving module‑boundary visibility and enabling better performance even when using bundlers.
Proposal link: https://github.com/littledan/proposal-module-fragments
// filename: app.js
module "#count" {
let i = 0;
export function count() {
i++;
return i;
}
}
module "#uppercase" {
export function uppercase(string) {
return string.toUpperCase();
}
}
import { count } from "#count";
import { uppercase } from "#uppercase";
console.log(count()); // 1
console.log(uppercase("daniel")); // "DANIEL"Module Fragments differ from the earlier Module Blocks proposal, which creates anonymous modules usable only via dynamic import() or new Worker(). Module Fragments are named, can be imported statically, and complement import‑maps for faster loading.
Conclusion
The JavaScript Chinese Interest Group (JSCIG) invites developers to discuss these and other ECMAScript topics on GitHub: esdiscuss .
Node Underground
No language is immortal—Node.js isn’t either—but thoughtful reflection is priceless. This underground community for Node.js enthusiasts was started by Taobao’s Front‑End Team (FED) to share our original insights and viewpoints from working with Node.js. Follow us. BTW, we’re hiring.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
