TC39

Ecma International's TC39 is a group of JavaScript developers, implementers, academics, and more, collaborating with the community to maintain and evolve the definition of JavaScript.

We are part of
Ecma International

Contribute

TC39 welcomes contributions. You can help by giving feedback on proposals, improving documentation, writing tests or implementations, or suggesting language feature ideas. See our contributor guide for details.

To participate in TC39 meetings as a member, join Ecma.

Specs

We develop the JavaScript (formally, ECMAScript) specification on GitHub and meet every two months to discuss proposals. To learn more about the process, please take a look at the four stages for new language feature proposals. See our meeting agendas and minutes to learn more.

State of Proposals

Current Candidates for the Specification

This section features proposals that are in Stage 3 of our process, which means they are close to completion.
How to read the proposals list

Each proposal has links out to other resources. The tags identify the following attributes. Below you will find a table with example tags and their meanings. All tags have hover text if you need help getting oriented.

Types of tags and what they mean

globalThis

Author and Champion: Jordan Harband
ECMAScript Proposal, specs, and reference implementation for globalThis

Legacy RegExp features in JavaScript

Author: Claude Pache | Champions: Mark Miller, Claude Pache
This is a specification draft for the legacy (deprecated) RegExp features in JavaScript, i.e., static properties of the constructor like RegExp.$1 as well as the RegExp.prototype.compile method.

BigInt

Author and Champion: Daniel Ehrenberg
Arbitrary precision integers in JavaScript
Show Example
const theBiggestInt = 9007199254740991n;
const alsoHuge = BigInt(9007199254740991); // ↪ 9007199254740991n
const hugeButString = BigInt('9007199254740991'); // ↪ 9007199254740991n

import.meta

Author and Champion: Domenic Denicola
A proposal for adding an import.meta metaproperty to JavaScript, for holding host-specific metadata about the current module.

Private instance methods and accessors

Author: Daniel Ehrenberg | Champions: Daniel Ehrenberg, Kevin Gibbons
Keeping state and behavior private to a class lets library authors present a clear, stable interface, while changing their code over time behind the scenes.
Show Example
class Counter extends HTMLElement {
  #xValue = 0;

  get #x() { return #xValue; }
  set #x(value) {
    this.#xValue = value;
    window.requestAnimationFrame(this.#render.bind(this));
  }

  #clicked() {
    this.#x++;
  }

  constructor() {
    super();
    this.onclick = this.#clicked.bind(this);
  }

  connectedCallback() { this.#render(); }

  #render() {
    this.textContent = this.#x.toString();
  }
} window.customElements.define('num-counter', Counter);

Class Public Instance Fields & Private Instance Fields

Authors: Daniel Ehrenberg, Kevin Gibbons | Champions: Daniel Ehrenberg, Jeff Morrison, Kevin Smith, Kevin Gibbons
This proposes a combined vision for public fields and private fields, drawing on the earlier Orthogonal Classes and Class Evaluation Order proposals.
Show Example
class Foo {
    instancePropertyBar = 0;
    static staticPropertyBar = 0;
    #privatePropertyBar = 0;
    static #privatePropertyBar = 0;

    set foo(value) { console.log(value); }
}

Static class fields and private static methods

Authors: Daniel Ehrenberg, Kevin Gibbons, Jeff Morrison, Kevin Smith | Champions: Shu-Yu Guo, Daniel Ehrenberg
This proposal adds Static public fields, Static private methods and Static private fields
Show Example
class ColorFinder {
  static #red = "#ff0000";

  static #blue = "#00ff00";

  static #green = "#0000ff";

  // ^ static class fields

  static white = "white";

  // ^ static public field.

  static colorName(name) {
    switch (name) {
      case "red": return ColorFinder.#red;
      case "blue": return ColorFinder.#blue;
      case "green": return ColorFinder.#green;
      default: throw new RangeError("unknown color");
    }
  }

  // Static method ^

  static #setColor(name) {

  }
  // ^ Static private method
}

Numeric Separators

Author and Champion: Sam Goto, Rick Waldron
This proposal adds the ability to utilize numeric literal separators for increased readability in Javascript source
Show Example
var a = 31_557_600; var b = 0b1111_1111; var c = 0xFF_FF_FF;

Promise.allSettled

Authors: Jason Williams, Robert Pamely, Mathias Bynens | Champion: Mathias Bynens
This proposal is to match popular usage in libraries, adds a method which returns a promise that is fulfilled with an array of promise state snapshots, but only after all the original promises have settled, i.e. become either fulfilled or rejected.
Show Example
const promises = [ fetch('index.html'), fetch('https://does-not-exist/') ];
const results = await Promise.allSettled(promises);
const successfulPromises = results.filter(p => p.status === 'fulfilled');

Hashbang Grammar

Author and Champion: Bradley Farias
This proposal is to match de-facto usage in some CLI JS hosts that allow for Shebangs / Hashbang. Such hosts strip the hashbang in order to generate valid JS source texts before passing to JS engines currently. This would unify and standardize how that is done..
Show Example
#!/usr/bin/env node
// in the Script Goal
'use strict';
console.log(1);

/****** begining of another script ********/
#!/usr/bin/env node
// in the Module Goal
export {};
console.log(1);

Top level await

Author and Champion: Myles Borins
Top-level await lets us rely on the module system itself to handle all of these promises, and make sure that things are well-coordinated.
Show Example
// awaiting.mjs
import { process } from "./some-module.mjs";
const dynamic = import(computedModuleSpecifier);
const data = fetch(url);
export const output = process((await dynamic).default, await data);
// usage.mjs
import { output } from "./awaiting.mjs";
export function outputPlusValue(value) { return output + value }
console.log(outputPlusValue(100));
setTimeout(() => console.log(outputPlusValue(100), 1000);

Weakrefs

Authors: Dean Tribble, Sathya Gunasekaran | Champions: Dean Tribble, Mark Miller, Till Schneidereit, Sathya Gunasekaran
Top-level await lets us rely on the module system itself to handle all of these promises, and make sure that things are well-coordinated.
Show Example
// Fixed version that doesn't leak memory.
function makeWeakCached(f) {

  const cache = new Map();

  const cleanup = new FinalizationGroup(iterator => {

    for (const key of iterator) {

      // See note below on concurrency considerations.

      const ref = cache.get(key);

      if (ref && !ref.deref()) cache.delete(key);
    }

  });

  return key => {

    const ref = cache.get(key);

    if (ref) {

      const cached = ref.deref();

      // See note below on concurrency considerations.

      if (cached !== undefined) return cached;

    }

    const fresh = f(key);

    cache.set(key, new WeakRef(fresh));

    cleanup.register(fresh, key, key);

    return fresh;

  };
}
var getImageCached = makeWeakCached(getImage);

Nullish Coalescing for JavaScript

Authors: Gabriel Isenberg, Daniel Ehrenberg, Daniel Rosenwasser | Champions: Gabriel Isenberg, Daniel Rosenwasser, Justin Ridgewell
When performing property accesses, it is often desired to provide a default value if the result of that property access is null or undefined. At present, a typical way to express this intent in JavaScript is by using the `||` operator. This introduces a `??` operator.
Show Example
const response = {

  settings: {

    nullValue: null,

    height: 400,

    animationDuration: 0,

    headerText: '',

    showSplashScreen: false

  }

};
const undefinedValue = response.settings.undefinedValue || 'some other default'; // result: 'some other default'
const nullValue = response.settings.nullValue || 'some other default'; // result: 'some other default'

RegExp Match array offsets

Author and Champion: Ron Buckton
ECMAScript RegExp Match Indicies provide additional information about the start and end indices of captured substrings relative to the start of the input string.
Show Example
const re1 = /a+(?z)?/;
// indices are relative to start of the input string:
const s1 = "xaaaz";
const m1 = re1.exec(s1);
m1.indices[0][0] === 1;
m1.indices[0][1] === 5;
s1.slice(...m1.indices[0]) === "aaaz";

m1.indices[1][0] === 4;
m1.indices[1][1] === 5;
s1.slice(...m1.indices[1]) === "z";

m1.indices.groups["Z"][0] === 4;
m1.indices.groups["Z"][1] === 5;
s1.slice(...m1.indices.groups["Z"]) === "z";

// capture groups that are not matched return `undefined`:
const m2 = re1.exec("xaaay");
m2.indices[1] === undefined;
m2.indices.groups["Z"] === undefined;

Optional Chaining

Authors: Claude Pache, Gabriel Isenberg, Dustin Savery | Champions: Gabriel Isenberg, Dustin Savery, Justin Ridgewell, Daniel Rosenwasser
The Optional Chaining Operator allows a developer to handle cases where if a property on an object is optional, they are able to check before accessing it.
Show Example
// Before
var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
// After, with optional chaining
var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value
See proposals in all stages