For AI agents: the complete documentation index is available at /tc39-atlas/en/llms.txt, the full documentation bundle is available at /tc39-atlas/en/llms-full.txt, and this page is available as Markdown at /tc39-atlas/en/proposals/stage/1/proposal-alias-accessors.md.
  • English
  • Alias Accessors S1

    Proposal details
    Proposal overview

    The proposal introduces 'alias accessors' syntax to reduce boilerplate when forwarding a class property to another property chain, addressing the common case where accessors are additive rather than arbitrary logic. It suggests syntax like alias foo to #foo.value as sugar for explicit getter/setter pairs, and explores variations for aggregation, read-only aliases, and integration with grouped and auto-accessors.

    Note

    The README below comes from the upstream repository and may contain outdated stage or status metadata. Use the proposal details above as the current source of truth.

    Alias Accessors

    Status

    Stage: 1 (as of the Jan 2026 plenary)

    Author/champion: Lea Verou (@leaverou)

    For history, see the original proposal.

    Contents

    1. Status
    2. Motivation
      1. Why not just use decorators?
    3. Design
      1. Aggregation
      2. Read-only aliases
      3. Integration with grouped and auto-accessors

    Motivation

    The vast majority (possibly over 90%, though it's hard to prove) of accessor use cases are additive. Their conceptual model is not entirely arbitrary logic, but a layering of transformations, side effects, and access control over a regular property (public or private).

    In some cases, this property is an internal implementation detail, and is never accessed separately. These cases are well served by auto-accessors together with built-in decorators.

    However, when a pre-existing property (often deeply nested) is used as the backing store, authors need to fall back to the usual boilerplate.

    class C {
    	#foo = new Signal(1);
    	get foo () {
    		return this.#foo.value;
    	}
    	set foo (value) {
    		this.#foo.value = value;
    	}
    }

    This proposal explores potential syntaxes with higher signal-to-noise ratio for these use cases, such as:

    class C {
    	#foo = new Signal(1);
    	alias foo to #foo.value;
    }

    Some (not mutually exclusive) high-level use cases for this are:

    • Encapsulation: obscure the data source so it can be changed later
    • Ergonomics: shorten frequently accessed property chains
    • Composition: Selectively expose API surface from other objects or first-class protocols
    • Access control: private properties with public getters

    Anecdotally:

    I'm looking at my accessors usage, and they are (numbers are not measured):

    • 75% "property forwarding"
    • 15% lazy initial computation
    • 10% validation
    • 5% other

    — Nicolò Ribaudo (@nicolo-ribaudo)

    Why not just use decorators?

    Private fields as targets is a very important use case, and there is currently no way to pass them around separately from the object they are defined on.

    But even for public fields, a decorator would be pretty awkward:

    class C {
    	// But we're not setting this to an array!
    	@alias accessor foo = ["a", "b", "c"];
    }
    class C {
    	// Better, but still confusing
    	@alias(["a", "b", "c"]) accessor foo;
    }

    Perhaps with something like reference declarations or similar it may become palatable:

    class C {
    	@alias accessor foo = x => ref x.a.#b.c;
    }

    or

    class C {
    	@alias(x => ref x.a.#b.c) accessor foo;
    }

    But it's still far from ideal.

    Design

    Just like auto-accessors, if no side effects or logic is desired, proxying another property should involve little additional syntax over the property name and a reference to the property (chain) holding the underlying value. It could even be a separate type of value-backed accessor, taking a property reference instead of an initial value, e.g.:

    class C {
    	somekeyword foo someseparator #foo.value;
    }

    This would be sugar for:

    class C {
    	get foo () {
    		return this.#foo.value;
    	}
    	set foo (value) {
    		this.#foo.value = value;
    	}
    }

    So the syntax to be bikeshedded is:

    1. Keyword to prepend the definition. Ideas:
      • alias
      • delegate
      • forward
      • derived
      • bind Could be confused with the bind method
      • proxy: Could be confused with the Proxy constructor
    2. Separator between the property name and the property chain being proxied. Ideas:
      • =: Could be confused with the assignment operator
      • =>: Could be confused with the arrow function operator, but also indicates a binding
      • via
      • from
      • through
      • to

    For concreteness, we’ll use alias/to in the rest of this document.

    These property chains are basically chains of [ . LiteralPropertyName ] | ComputedPropertyName ]. this. at the start is implicit.

    They are not References however: conceptually, the entire chain is re-evaluated on each access.

    Aggregation

    There are many cases where multiple properties need to be forwarded through another object. For example, when using delegation/forwarding patterns, such as ElementInternals, multiple properties need to be exposed.

    Current syntaxPotential new syntax
    class MyElement extends HTMLElement {
    	#internals = this.attachInternals();
    	static formAssociated = true;
    
    	// Expose
    	get form () {
    		return this.#internals.form;
    	}
    	get labels () {
    		return this.#internals.labels;
    	}
    	get checkValidity () {
    		return this.#internals.checkValidity;
    	}
    	get reportValidity () {
    		return this.#internals.reportValidity;
    	}
    	get willValidate () {
    		return this.#internals.willValidate;
    	}
    	get validationMessage () {
    		return this.#internals.validationMessage;
    	}
    	// ...
    }
    class MyElement extends HTMLElement {
    	#internals = this.attachInternals();
    	static formAssociated = true;
    
    	// Expose
    	alias form              = #internals.form;
    	alias labels            = #internals.labels;
    	alias checkValidity     = #internals.checkValidity;
    	alias reportValidity    = #internals.reportValidity;
    	alias willValidate      = #internals.willValidate;
    	alias validationMessage = #internals.validationMessage;
    	// ...
    }

    or when implementing a first-class protocol:

    Current syntaxPotential new syntax
    class C implements Iterable {
    	get forEach () {
    		return this[Iterable.forEach];
    	}
    	get map () {
    		return this[Iterable.map];
    	}
    	get flatMap () {
    		return this[Iterable.flatMap];
    	}
    	get filter () {
    		return this[Iterable.filter];
    	}
    	get reduce () {
    		return this[Iterable.reduce];
    	}
    	get toArray () {
    		return this[Iterable.toArray];
    	}
    	get some () {
    		return this[Iterable.some];
    	}
    	get every () {
    		return this[Iterable.every];
    	}
    	get find () {
    		return this[Iterable.find];
    	}
    	static get from () {
    		return this[Iterable.from];
    	}
    	// ...
    }
    class C implements Iterable {
    	alias forEach = [Iterable.forEach];
    	alias map     = [Iterable.map];
    	alias flatMap = [Iterable.flatMap];
    	alias filter  = [Iterable.filter];
    	alias reduce  = [Iterable.reduce];
    	alias toArray = [Iterable.toArray];
    	alias some    = [Iterable.some];
    	alias every   = [Iterable.every];
    	alias find    = [Iterable.find];
    	alias from    = [Iterable.from];
    	// ...
    }

    While alias improves things quite a lot already, it’s still a little repetitive. There is not much to do in the second case, but perhaps there could be a destructuring-inspired shortcut when the names are identical:

    class C {
    	alias {
    		form,
    		labels,
    		checkValidity,
    		reportValidity,
    		willValidate,
    		validationMessage
    	} from #internals;
    }

    Read-only aliases

    While by default both a setter and a getter would be added, composable setters could be used to make it read-only, either with silent rejection (DOM style) or loud rejection (JS style):

    class C {
    	#foo = 1;
    	@validate(v => false)
    	alias #foo to foo;
    }

    Do note however that this is only needed if the property being proxied is read-only, the error would just propagate naturally:

    class MyElement extends HTMLElement {
    	#internals = this.attachInternals();
    
    	// ElementInternals.prototype.form is read-only
    	// so setting form will throw
    	alias form = #internals.form;
    }

    Here, setting myElement.form would produce an error anyway, since ElementInternals.prototype.form is read-only. Therefore, there is no need to prevent writes explicitly unless we want to swallow them.

    Integration with grouped and auto-accessors

    Another direction for this proposal might be to become a syntax extension of grouped and auto-accessors. Customization of the backing store was already requested in #14.

    The syntax proposed in the issue was accessor(#x) x = 1;. Syntax like accessor x : #x = 1 would likely clash with TS.