Fundamentals 13 min read

What New TC39 Proposals Are Shaping JavaScript’s Future?

Recent TC39 Stage‑1 proposals introduce iterator deduplication, enhanced template literals, function and object literal decorators, concurrency controls for async iterators, unordered async helpers, immutable ArrayBuffers, a measurement API, Array.zip utilities, reactive Signals, and stricter resource‑management using enforcement, each aiming to extend JavaScript’s core capabilities.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
What New TC39 Proposals Are Shaping JavaScript’s Future?

When a proposal reaches Stage 1, its value and design are officially accepted by TC39 and the standardisation process begins.

Iterator Unique

Proposal address: proposal-iterator-unique.

Deduplicating items in a collection is common, but for JavaScript iterators it adds complexity. A simple Set‑based approach works but has drawbacks such as consuming the entire iterator, not supporting infinite iterators, mishandling -0, and failing on non‑iterable iterators.

<code>let uniques = new Set(iter).values();</code>

The proposal adds

Iterator.prototype.uniqBy

with an optional mapping function to perform deduplication.

<code>let uniques = iter.uniqBy();
let uniques = iter.uniqBy(obj => obj.field);</code>

Improved Escapes for Template Literals

Proposal address: proposal-improve-template-literals.

Current template strings require manual escaping of special characters using

String.raw

or

String.dedent

, yet characters like backticks or

${

are not handled correctly.

<code>let query = `
    select *
    from \`users\`
    where \`name\` = ?
`;</code>

The proposal introduces a new template literal syntax that eliminates the need for manual escaping.

<code>// syntax TBD, just use @sken130's strawperson draft as demo
let query = @``
    select *
    from \`users\`
    where \`name\` = ?
    ``</code>

Function and Object Literal Element Decorators

Proposal address: proposal-function-and-object-literal-element-decorators.

The proposal enables decorator syntax on function expressions, function declarations, and object literals.

<code>// logging/tracing
@logged
function doWork() { ... }

// utility wrappers
const onpress = @debounce(250) (e) => console.log("button pressed: ", e.pressed);

// metadata
@ParamTypes(() => [Number, Number])
@ReturnType(() => Number)
function add(x, y) { return x + y; }

// React Functional Components
@withStyles({
  root: { border: '1px solid black' },
})
@React.forwardRef
function Button(props, forwardedRef) { ... }

const addOne = (_target, _context) => x => x + 1;
const obj = { @addOne x: 2 };
console.log(obj.x); // 3</code>

Concurrency Control

Proposal address: proposal-concurrency-control.

Extends async‑iterator‑helpers with a

limit

parameter and a flexible

CountingGovernor

to provide simple concurrency control.

Unordered Async Iterator Helpers

Proposal address: proposal-unordered-async-iterator-helpers.

Unlike ordered async‑iterator helpers such as

forEach

or

some

, unordered helpers relax ordering constraints, potentially improving performance and throughput.

Immutable ArrayBuffers

Proposal address: https://github.com/tc39/proposal-immutable-arraybuffer.

Current

ArrayBuffer

can be resized or transferred; this proposal adds immutable buffers via

transferToImmutable()

and a read‑only

immutable

property.

transferToImmutable()

: moves the current buffer’s contents to a new immutable buffer, detaching the original.

immutable

: read‑only flag indicating immutability.

Immutable buffers cannot be detached, resized, or transferred, and their maximum byte length equals the current length. TypedArray and DataView views over an immutable buffer can be frozen.

<code>const consumeIntoNetstring = data => {
  // Transfer to a new ArrayBuffer with room for the netstring framing.
  const prefix = new TextEncoder().encode(`${data.length}:`);
  const buf = data.buffer.transfer(prefix.length + data.length + 1);

  // Frame the data.
  const tmpArr = new Uint8Array(buf);
  tmpArr.copyWithin(prefix.length, 0);
  tmpArr.set(prefix);
  tmpArr[tmpArr.length - 1] = 0x2C;

  // Transfer to an immutable ArrayBuffer backing a frozen Uint8Array.
  const frozenNetstring = Object.freeze(new Uint8Array(buf.transferToImmutable()));
  assert(buf.detached);
  return frozenNetstring;
};

const input = new TextEncoder().encode('hello world!');
const result = consumeIntoNetstring(input);
assert(Object.isFrozen(result));
try { result[0] = 0; } catch (_) {}
try { new Uint8Array(result.buffer)[0] = 1; } catch (_) {}
try { result.buffer.transferToImmutable(); } catch (_) {}
assert(String.fromCharCode(...result) === '12:hello world!,');</code>

Measure

Proposal address: https://github.com/ben-allen/proposal-measure.

Introduces a

Measure

object to represent values with units and convert between them.

unit

: unit string.

value

: primary unit numeric value.

minorValue

: secondary unit value, if any.

Prototype methods:

convert(unit, precision)

: converts to the given unit.

localeConvert(locale, usage)

: converts based on locale data from CLDR’s

units.xml

.

<code>let m = new Measure(1.8, "meter");
m.convert('foot', 2); // { value: 5.91, unit: "foot" }
m.localeConvert("en-CA", "personHeight"); // { value: 5, minorValue: 11 }

let m2 = new Measure(4000, "foot");
m2.convert("kilometer"); // { value: 1.2192, unit: "kilometer" }
m2.localeConvert('en-US', "road"); // { value: 0.76, unit: "mile" }</code>

Array.zip

Proposal address: proposal-array-zip.

Adds static methods

Array.zip

and

Array.zipKeyed

to synchronize iteration over multiple arrays, based on the joint‑iteration proposal.

Signals

Proposal address: proposal-signals.

Signals provide a reactive programming model that automatically tracks dependencies, lazily evaluates, and memoizes results, aiming to simplify state‑update management across frameworks.

<code>const counter = new Signal.State(0);
const isEven = new Signal.Computed(() => (counter.get() & 1) == 0);
const parity = new Signal.Computed(() => isEven.get() ? "even" : "odd");

declare function effect(cb: () => void): () => void;

effect(() => element.innerText = parity.get());
setInterval(() => counter.set(counter.get() + 1), 1000);
</code>

The proposal was discussed in April 2024 and remains in early stages.

Strict using

Proposal address: proposal-using-enforcement.

Builds on explicit‑resource‑management, enforcing that resources implementing

Symbol.enter

must be used with a

using

statement, ensuring proper cleanup.

JavaScriptiteratorsResource ManagementReactive ProgrammingTC39Language Proposals
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

login 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.