ECMAScript class static blocks
Switch branches/tags
Nothing to show
Clone or download

README.md

ECMAScript class static initialization blocks

Class static blocks provide a mechanism to perform additional static initialization during class definition evaluation.

This is not intended as a replacement for public fields, as they provide useful information for static analysis tools and are a valid target for decorators. Rather, this is intended to augment existing use cases and make enable new use cases not currently handled by that proposal.

Status

Stage: 1
Champion: Ron Buckton (@rbuckton)

For detailed status of this proposal see TODO, below.

Authors

  • Ron Buckton (@rbuckton)

Motivations

The current proposals for static fields and static private fields provide a mechanism to perform per-field initialization of the static-side of a class during ClassDefinitionEvaluation, however there are some cases that cannot be covered easily. For example, if you need to evaluate statements during initialization (such as try..catch), you have to perform that logic outside of the class definition.

// without static blocks:
class C {
  static x = ...;
  static y;
}

try {
  C.y = doSomethingWith(C.x);
}
catch {
  C.y = ...;
}

// with static blocks:
class C {
  static x = ...;
  static y;
  static {
    try {
      this.y = doSomethingWith(this.x);
    }
    catch {
      this.y = ...;
    }
  }
}

In addition, there are cases where information sharing needs to occur between a class with an instance private field and another class or function declared in the same scope.

Static blocks provide an opportunity to evaluate statements in the context of the current class declaration, with privileged access to private state (be they instance-private or static-private):

let getX;

export class C {
  #x
  constructor(x) {
    this.#x = { data: x };
  }

  static {
    // getX has privilged access to #x
    getX = (obj) => obj.#x;
  }
}

export function readXData(obj) {
  return getX(obj).data;
}

Prior Art

Syntax

class C {
  static {
    // statements
  }
}

Semantics

  • A static {} block creates a new lexical scope (e.g. var, function, and block-scoped declarations are local to the static {} block. This lexical scope is nested within the lexical scope of the class body (granting privileged access to instance private state for the class).
  • A class may have at most one static {} block in its class body.
  • A static {} block is evaluated immediately after all public static field initializers have been evaluated as part of class declaration evaluation, regardless of its order within the class body (this aligns with constructor() {}).
  • A static {} block may not have decorators (instead you would decorate the class itself). Decorators can always add a class finisher to add their own static initialization.
  • When evaluated, a static {} block's this receiver is the constructor object of the class (as with static field initializers).

Examples

// "friend" access (same module)
let A, B;
{
  let friendA;

  A = class A {
    #x;

    static {
        friendA = {
          getX(obj) { return obj.#x },
          setX(obj, value) { obj.#x = value }
        };
    }
  };

  B = class B {
    constructor(a) {
      const x = friendA.getX(a); // ok
      friendA.setX(a, value); // ok
    }
  };
}

TODO

The following is a high-level list of tasks to progress through each stage of the TC39 proposal process:

Stage 1 Entrance Criteria

  • Identified a "champion" who will advance the addition.
  • Prose outlining the problem or need and the general shape of a solution.
  • Illustrative examples of usage.
  • High-level API.

Stage 2 Entrance Criteria

Stage 3 Entrance Criteria

Stage 4 Entrance Criteria

  • Test262 acceptance tests have been written for mainline usage scenarios and merged.
  • Two compatible implementations which pass the acceptance tests: [1], [2].
  • A pull request has been sent to tc39/ecma262 with the integrated spec text.
  • The ECMAScript editor has signed off on the pull request.