Skip to content
Private Declarations Proposal at Stage 1
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

Private Declarations

A Stage 1 proposal to add Private Declarations, allowing trusted code outside of the class lexical scope to access private state.

private #hello;
class Example {
  [#hello] = 'world!';

const ex = new Example();
// => 'world!'

Guiding Use Cases

"Protected" state

Protected state is a valuable visibility state when implementing class hierarchies. For for instance, a hook pattern might be used to allow subclasses to override code:


private #createPart;

class AttributeCommitter {

  [#createPart]() {
    return new AttributePart(this);

class PropertyCommitter extends AttributeCommitter {
  [#createPart]() {
    return new PropertyPart(this);

Here, both AttributeCommitter explicitly allows trusted (written in the same source file) subclasses to override the behavior of the #createPart method. By default, a normal AttributePart is return. But PropertyCommitter works only on properties and would return a PropertyPart. All other code is free to be inherited via normal publicly visible fields/methods from AttributeCommitter.

Note that this does not privilege code outside the file to override the #createPart method, the #createPart declaration is visible only to the file.

Friend classes/functions

For prior art in C++, see

The AMP Project has a particular staged linting pattern that works well with friendly functions that guards access to restricted code. To begin with, statically used functions are considerably easier to lint for than object-scoped method calls because we do not need to know the objects type to determine if this is a restricted call or just a non-restricted call that uses the same method name. Eg, it's easier to tell that a static export registerExtendedTemplate is restricted vs obj.registerExtendedTemplate.


private #registerTemplate;

// Exported so that it may be intalled on the globally and shared
// across split bundles.
export class Templates {
  [#registerTemplate]() {

// The code privileged to register templates with the shared class
// instance. Importing and using is statically analyzable, and must pass
// a linter.
export function registerExtendedTemplate() {
  const templatesService = getService('templates');
  return templatesService.#registerTemplate(...arguments);
You can’t perform that action at this time.