Fundamentals 25 min read

What New JavaScript Proposals Emerged from TC39’s January Meeting?

The article reviews TC39’s January 29 meeting, detailing the criteria for advancing proposals between stages, summarizing key proposals such as Intl.DateTimeFormat.formatRange, JSON Modules, Private‑in operator, Class static initializer blocks, ResizableArrayBuffer, Intl.LocaleInfo, RegExp enhancements, and the community’s role in shaping ECMAScript.

Node Underground
Node Underground
Node Underground
What New JavaScript Proposals Emerged from TC39’s January Meeting?

TC39’s January 29 meeting concluded with a review of proposals that achieved stage progress and a summary of recent changes.

Stage 3 → Stage 4

Advancing from Stage 3 to Stage 4 requires:

Writing tc39/test262 tests covering the entire proposal and ensuring the tests are merged.

At least two implementations must pass the Test262 suite and ship in a formal release.

Submitting a pull request that merges the proposal into the official ECMA262 text and obtaining a signed editorial agreement.

Relevant links:

tc39/test262

tc39/ecma262

Intl.DateTimeFormat.prototype.formatRange

This proposal adds the ability to format date ranges in internationalized contexts. It introduces two methods: Intl.DateTimeFormat.prototype.formatRange – returns a formatted string for a date range. Intl.DateTimeFormat.prototype.formatRangeToParts – returns an array of objects describing the formatted parts.

let date1 = new Date(Date.UTC(2007, 0, 10, 10, 0, 0));
let date2 = new Date(Date.UTC(2007, 0, 10, 11, 0, 0));
let fmt = new Intl.DateTimeFormat("en", { hour: "numeric", minute: "numeric" });
fmt.formatRange(date1, date2); // "10:00 – 11:00 AM"
fmt.formatRangeToParts(date1, date2); // [{type:"hour",value:"10",source:"startRange"}, …]

The proposal is already usable in Chrome 79 and Node.js 12.9.0.

JSON Modules

The JSON Modules proposal separates the JSON handling part from the Import Assertion proposal, allowing modules to be imported as JSON with explicit type assertions.

import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });

Importing non‑JSON files with this syntax will cause a module‑resolution error, addressing earlier security concerns.

Private Fields In Operator

This proposal introduces the in operator for checking the existence of private fields without triggering a try/catch pattern.

class C {
  #brand;
  static isC(obj) { return #brand in obj && #method in obj && #getter in obj; }
}

It clarifies that private fields remain inaccessible outside their lexical scope and that the in check respects the distinct identity of private fields across different class definitions.

Stage 2 → Stage 3

Advancing from Stage 2 to Stage 3 requires:

Writing a complete standard text reviewed and signed by a TC39 member.

Obtaining a signed editorial agreement from the ECMAScript editor.

Class Static Initializer Block

This proposal adds static initializer blocks that run once when a class is evaluated, providing access to the class’s private fields and enabling patterns such as “friend” classes.

export class C {
  #x;
  constructor(x) { this.#x = { data: x }; }
  static {
    // privileged access to #x
    getX = obj => obj.#x;
  }
}
export function readXData(obj) { return getX(obj).data; }

Stage 1 → Stage 2

Advancing from Stage 1 to Stage 2 requires drafting the full proposal text.

JavaScript Module Blocks

Addresses the need for isolated code blocks that can be parsed once and reused across workers, improving safety and performance.

let workerCode = module {
  onmessage = function({ data }) {
    let mod = await import(data);
    postMessage(mod.fn());
  };
};
let worker = new Worker(workerCode, { type: 'module' });
worker.onmessage = ({ data }) => alert(data);
worker.postMessage(module { export function fn() { return 'hello!'; } });

ResizableArrayBuffer and GrowableSharedArrayBuffer

Introduces two new buffer types to enable efficient memory resizing without copying.

let rab = new ResizableArrayBuffer(1024, 1024 ** 2);
assert(rab.byteLength === 1024);
assert(rab.maximumByteLength === 1024 ** 2);
rab.resize(rab.byteLength * 2);
assert(rab.byteLength === 2048);

GrowableSharedArrayBuffer can only increase in size and is shareable across multiple execution contexts.

Intl.LocaleInfo

Provides APIs to query locale‑specific preferences such as the first day of the week, weekend range, and text direction.

let zhHans = new Intl.Locale("zh-Hans");
zhHans.weekInfo; // {firstDay:1, weekendStart:6, weekendEnd:7, minimalDays:4}
zhHans.textInfo; // {direction:"ltr"}

Intl.DisplayNames (v2)

Extends Intl.DisplayNames to support months, weekdays, units, time zones, calendars, and numbering systems.

let dn = new Intl.DisplayNames("zh-Hans", { type:"month", style:"long" });
 dn.of(1); // "1月"
let dn2 = new Intl.DisplayNames("en", { type:"month", style:"long", calendar:"coptic" });
 dn2.of(1); // "Tout"

Async do expressions

Adds async semantics to do‑expressions, allowing await inside non‑async contexts and defining clear completion‑value rules.

Promise.all([
  async do { let r = await fetch('thing A'); await r.json(); },
  async do { let r = await fetch('thing B'); await r.json(); }
]).then(([a, b]) => console.log([a, b]));

Class Brand Checks

Provides a way to verify that an object is a genuine instance of a class, even when prototype chains are altered.

class C { static isC = o => class.hasInstance(o); }
C.isC({}); // false
C.isC(new C()); // true

RegExp set notation

Introduces set operations (union, intersection, difference) for character classes in regular expressions.

// Difference
[A--B]
// Intersection
[A&&B]
// Nested set
[A--[0-9]]

Revisiting RegExp escape

Adds RegExp.escape to safely escape user‑provided strings for use in regular expressions.

RegExp.escape('Buy it. use it.'); // "Buy it\. use it\."
RegExp.escape('(*.*)'); // "\(\*\.\*\)"

Array find from last

Introduces Array.prototype.findLast and Array.prototype.findLastIndex to search arrays from the end.

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

Defer module import eval

Allows lazy initialization of imports via a with { lazyInit: true } syntax, avoiding async/await propagation for rarely used modules.

import {x} from "y" with { lazyInit: true };

Extend TimeZoneName Option

Expands the timeZoneName option in Intl.DateTimeFormat to support additional formats such as shortGMT, longGMT, shortWall, and longWall.

let names = ["short","long","shortGMT","longGMT","shortWall","longWall"];
names.forEach(tz => console.log(new Date().toLocaleTimeString("zh-Hans", {timeZoneName: tz})));

EraDisplay

Adds an eraDisplay option to control era formatting in Intl.DateTimeFormat.

new Intl.DateTimeFormat("zh-Hans").format(new Date(-752,3,13)); // "753/4/13"
// With eraDisplay "always"
// "公元前 753/4/13 BC"

Intl LocaleMatcher

Exposes a JavaScript API to match the best available locale given a list of requested locales.

Intl.LocaleMatcher.match(["zh-Hans","en"], ["zh","en"], "en"); // "zh"

The meeting also highlighted the formation of the JavaScript Chinese Interest Group (JSCIG), which aims to represent Chinese developers in TC39 discussions and foster community contributions.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ECMAScriptstageTC39proposalsintl
Node Underground
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.