Fundamentals 24 min read

Which New JavaScript Proposals Advanced to Stage 4 in the Jan 2024 TC39 Meeting?

The January 29 TC39 meeting reviewed progress on dozens of ECMAScript proposals, outlining the criteria for moving from Stage 3 to Stage 4, Stage 2 to Stage 3, and Stage 1 to Stage 2, and highlighting key features such as Intl.DateTimeFormat.formatRange, JSON Modules, Private‑in, class static blocks, resizable array buffers, and new Intl APIs.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Which New JavaScript Proposals Advanced to Stage 4 in the Jan 2024 TC39 Meeting?

The TC39 meeting held on January 29 concluded with a detailed review of proposal progress, summarising the requirements for advancing from Stage 3 to Stage 4, Stage 2 to Stage 3, and Stage 1 to Stage 2, and providing links to the relevant GitHub repositories.

Stage 3 → Stage 4

All proposal content must have corresponding tc39/test262 tests that are merged into the proposal.

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

A pull request that inserts the proposal text into tc39/ecma262 must be signed off by the ECMAScript editor.

https://github.com/tc39/test262 https://github.com/tc39/ecma262

Intl.DateTimeFormat.prototype.formatRange

This proposal adds the ability to format date ranges for internationalized contexts. It introduces two methods: Intl.DateTimeFormat.prototype.formatRange (returns a string) and Intl.DateTimeFormat.prototype.formatRangeToParts (returns an array of part objects).

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 proposal separates the JSON module portion from the Import Assertion proposal, allowing ECMAScript modules to import JSON files with an explicit type assertion.

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

Incorrect file types cause a module‑resolution error, and the type is determined by the assertion rather than the HTTP Content‑Type header.

Private Fields In Operator

This proposal adds the in operator for private fields, enabling a concise check for the existence of a private field on an object without using try { obj.#field } catch {}.

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

It does not change the semantics of private fields themselves; they remain inaccessible outside their lexical class scope.

Class Static Initializer Block

The proposal introduces static initialization blocks that run once when a class is evaluated, with full access to the class’s private fields, enabling patterns such as “friend” classes.

let getX;
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 2 → Stage 3

The proposal text must be written and reviewed by a TC39 member.

The ECMAScript editor must sign off on the proposal.

Class Brand Checks

This proposal adds a reliable way to test whether an object is a genuine instance of a class, even when the prototype chain has been altered.

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

ResizableArrayBuffer and GrowableSharedArrayBuffer

Two new buffer types are proposed to improve memory handling for streaming and WebAssembly scenarios.

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);

Intl.LocaleInfo

The proposal adds APIs to retrieve 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

Extends Intl.DisplayNames to cover 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"

RegExp set notation

Introduces set operators for regular‑expression character classes, allowing union, intersection, and difference.

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

Revisiting RegExp escape

Proposes a standard RegExp.escape function that safely escapes any string for use in a regular expression.

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

Array find from last

Adds Array.prototype.findLast and Array.prototype.findLastIndex to search an array 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 static import statements to be marked as lazy, avoiding the propagation of async/await when the module is rarely used.

import {x} from "y" with { lazyInit: true };
import defaultName from "y" with { lazyInit: true };
import * as ns from "y" with { lazyInit: true };

Extend TimeZoneName Option

Extends the timeZoneName option of Intl.DateTimeFormat to support additional formats such as shortGMT, longWall, etc.

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

EraDisplay

Introduces an eraDisplay option to control how era information is formatted.

new Intl.DateTimeFormat("zh-Hans").format(new Date(-752,3,13)); // before: "753/4/13"
// with proposal: "公元前 753/4/13 BC"

Intl LocaleMatcher

Exposes the locale‑matching algorithm defined in ECMA‑402 as a JavaScript API.

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 invites community participation via GitHub.

TC39 meeting diagram
TC39 meeting diagram
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.

JavaScriptECMAScriptTC39intlStage 4
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

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.