Explore the Latest TC39 Proposals: Decorators, Type Annotations, and Immutable Array Methods

This article reviews recent TC39 proposal progress—including decorators reaching Stage 3, type annotations advancing to Stage 1, new immutable array methods, RegExp set notation, decorator metadata, and function.once—detailing their syntax, semantics, migration considerations, and example implementations for modern JavaScript development.

Alibaba Terminal Technology
Alibaba Terminal Technology
Alibaba Terminal Technology
Explore the Latest TC39 Proposals: Decorators, Type Annotations, and Immutable Array Methods

In the recent TC39 meeting, the decorator proposal advanced to Stage 3 and the Type Annotations proposal entered Stage 1; no proposal moved from Stage 3 to Stage 4.

Stage 2 → Stage 3 Requirements

To promote a proposal from Stage 2 to Stage 3, the following must be satisfied:

A complete standard text is written, reviewed, and signed off by designated TC39 members.

The ECMAScript editor signs off on the proposal.

Decorators

Decorator syntax is widely used in JavaScript/TypeScript. The current proposal is the third iteration and differs from the legacy TypeScript implementation.

A decorator is a function that can modify a class or class member at definition time.

type Decorator = (value: Input, context: {
  kind: string;
  name: string | symbol;
  access: {
    get?(): unknown;
    set?(value: unknown): void;
  };
  isPrivate?: boolean;
  isStatic?: boolean;
  addInitializer?(initializer: () => void): void;
}) => Output | void;

The value argument is the target being decorated; context provides metadata such as kind, name, access, isStatic, isPrivate, and addInitializer.

kind : type of the decorated element (class, method, field, etc.).

name : name of the element.

access : getter/setter for the element.

isStatic / isPrivate : visibility flags.

addInitializer : registers code to run during class instantiation.

New calling conventions include allowing decorators on class expressions and adjusting the syntax when used with export:

const Foo = @deco class {};
export default @deco class Bar {};

Class Decorator

type ClassDecorator = (value: Function, context: {
  kind: "class";
  name: string | undefined;
  addInitializer(initializer: () => void): void;
}) => Function | void;

A class decorator can replace the class entirely or return a subclass:

function logged(value, {kind, name}) {
  if (kind === "class") {
    return class extends value {
      constructor(...args) { super(...args); }
    };
  }
}
@logged
class C {}

Class Method Decorator

type ClassMethodDecorator = (value: Function, context: {
  kind: "method";
  name: string | symbol;
  access: { get(): unknown };
  isStatic: boolean;
  isPrivate: boolean;
  addInitializer(initializer: () => void): void;
}) => Function | void;

Method decorators can replace the method or wrap it to add extra logic:

function logged(value, {kind, name}) {
  if (kind === "method") {
    return function(...args) {
      const ret = value.call(this, ...args);
      return ret;
    };
  }
}
class C {
  @logged
  m(arg) {}
}

Class Accessor Decorator

type ClassGetterDecorator = (value: Function, context: {
  kind: "getter";
  name: string | symbol;
  access: { get(): unknown };
  isStatic: boolean;
  isPrivate: boolean;
  addInitializer(initializer: () => void): void;
}) => Function | void;

type ClassSetterDecorator = (value: Function, context: {
  kind: "setter";
  name: string | symbol;
  access: { set(value: unknown): void };
  isStatic: boolean;
  isPrivate: boolean;
  addInitializer(initializer: () => void): void;
}) => Function | void;

Getter and setter decorators receive the original accessor functions and may replace them.

class C {
  @foo
  get x() { /* ... */ }

  set x(val) { /* ... */ }
}

Class Field Decorator

type ClassFieldDecorator = (value: undefined, context: {
  kind: "field";
  name: string | symbol;
  access: { get(): unknown; set(value: unknown): void };
  isStatic: boolean;
  isPrivate: boolean;
}) => (initialValue: unknown) => unknown | void;

Field decorators receive undefined as value; they can return a function that transforms the initial value.

function logged(value, {kind, name}) {
  if (kind === "field") {
    return function(initialValue) {
      return 599;
    };
  }
}
class C {
  @logged x = 1;
}
new C().x; // 599

Auto Accessor

The proposal introduces the accessor keyword for auto‑accessors, which store values in a private slot and expose getter/setter semantics.

class C {
  accessor x = 1;
}

Compiled equivalent:

class C {
  #x = 1;
  get x() { return this.#x; }
  set x(v) { this.#x = v; }
}

Decorator Metadata

A separate metadata proposal (Stage 2) adds a metadataKey to decorator contexts, enabling storage of metadata on classes and members.

interface MetadataKey { parent: MetadataKey | null; }

type Decorator = (value: Input, context: {
  kind: string;
  name: string | symbol;
  access: { get?(): unknown; set?(value: unknown): void };
  isPrivate?: boolean;
  isStatic?: boolean;
  addInitializer?(initializer: () => void): void;
  metadataKey?: MetadataKey;
  class?: { metadataKey: MetadataKey; name: string };
}) => Output | void;

Example usage:

const METADATA = new WeakMap();
function meta(value) {
  return (_, context) => {
    METADATA.set(context.metadataKey, value);
  };
}
@meta('a')
class C {
  @meta('b')
  foo;
  @meta('c')
  get bar() {}
  @meta('d')
  baz() {}
}
METADATA.get(C[Symbol.metadataKey]); // class metadata
METADATA.get(C.foo[Symbol.metadata]); // 'b'

Stage 1 → Stage 2 Transition

Advancing from Stage 1 to Stage 2 requires a complete draft specification, a champion TC39 member, clear problem statement, examples, and analysis of API, algorithms, and risks.

Change Array by Copy

This proposal adds immutable counterparts to mutating array methods:

Array.prototype.toReversed

Array.prototype.toSorted

Array.prototype.toSpliced

Array.prototype.with

TypedArray prototypes receive the same methods. Polyfills are available via CoreJS or ES‑Shims.

RegExp Set Notation

Introduces & syntax for set operations inside character classes, enabling intersection, difference, and nesting:

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

Examples:

[\p{Decimal_Number}--[0-9]]
[\p{Emoji}--\p{ASCII}]

Type Annotations

The most discussed proposal adds native static type syntax to JavaScript, mirroring TypeScript. It includes basic type annotations, interfaces, type imports/exports, type aliases, type assertions, generics, and the :: syntax to disambiguate generic calls.

import type { Foo } from "foo";
let x: string;
function equals(x: number, y?: number): boolean { return x === y; }
interface Person { name: string; age: number; }
export type CoolBool = boolean;

Only core type features are targeted; advanced TypeScript constructs like declaration files, overloads, and private fields are out of scope for now.

Function.prototype.once

This proposal adds a native once method to functions, returning a version that executes only on the first call and thereafter returns the cached result.

function f(x) { console.log(x); return x * 2; }
const fOnce = f.once();
fOnce(3); // logs 3, returns 6
fOnce(3); // no log, returns 6

Intl.MessageFormat

Extends the Intl API with Intl.MessageFormat to support MessageFormat 2.0, enabling locale‑aware message selection and formatting.

const resource = {
  new_notifications: {
    0: "你现在还没有信息",
    one: "你收到新信息了~",
    _: "你收到了 { $count } 条新信息,快打开看看吧!"
  }
};
const mf = new Intl.MessageFormat(resource, ['en']);
const msg = mf.resolveMessage('new_notifications', { count: 3 });
msg.toString(); // "你收到了 3 条新信息,快打开看看吧!"

Conclusion

The JavaScript Chinese Interest Group (JSCIG) continues to host discussions on ECMAScript proposals. Interested developers are encouraged to join the conversation on GitHub: https://github.com/JSCIG/es-discuss/discussions.

JavaScriptdecoratorsType AnnotationsTC39proposalimmutable arrays
Alibaba Terminal Technology
Written by

Alibaba Terminal Technology

Official public account of Alibaba Terminal

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.