HackerReposA FIELD GUIDE TO OPEN SOURCE
ProSponsor the shelf♧ 0
← Back to top all-time
tc39
proposal-class-fields
HTML2017–2023
tc39 / proposal-class-fields · Archived

proposal-class-fields

Orthogonally-informed combination of public and private fields proposals

32Distinct people
39Mentions
1Explicit recommenders
1.7KGitHub stars
REPOSITORY AT A GLANCE

What to know before exploring

What it is

Orthogonally-informed combination of public and private fields proposals

GitHub

Where it is used

No documented product use was verified in the last source check.

What people are saying

Watching how the language grows can be very interesting. I highly recommend taking a look at TC39 proposals and the process. For example, SmooshGate:…

otras used a recommendation phrase. One indexed comment matches. This is a phrase match, not a verdict.

No classified warning is attached to this page. Read the discussions before treating silence as approval.

Also in these discussions

Mentioned in the same Hacker News threads. That is not proof they are substitutes.

This week on Hacker News

Nov 7–Nov 14 UTC · Direct repository links and separately labeled related project stories. Indexed through 11/14/2026, 7:02:50 PM UTC.

No direct repository links were found in the indexed HN discussions over the past seven days. Older discussions are available below.

Mentions over time

HN mentions by year39 dated mentions

Observed mentions: 2017-08-03 17:19:41 UTC to 2023-05-30 07:30:05 UTC.

051015202017-01-01 00:00:00 UTC to 2018-01-01 00:00:00 UTC (exclusive): 5 mentions; 1 submissions, 4 comments20172018-01-01 00:00:00 UTC to 2019-01-01 00:00:00 UTC (exclusive): 20 mentions; 1 submissions, 19 comments2019-01-01 00:00:00 UTC to 2020-01-01 00:00:00 UTC (exclusive): 10 mentions; 0 submissions, 10 comments2020-01-01 00:00:00 UTC to 2021-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments20202021-01-01 00:00:00 UTC to 2022-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2022-01-01 00:00:00 UTC to 2023-01-01 00:00:00 UTC (exclusive): 0 mentions; 0 submissions, 0 comments2023-01-01 00:00:00 UTC to 2024-01-01 00:00:00 UTC (exclusive): 4 mentions; 2 submissions, 2 comments2023

Counts come from the repository’s dated source evidence. Index cutoff: 2026-11-02 08:16:59 UTC.

View mention counts and date ranges
UTC ranges include their start and exclude their end. Select a period to filter the source evidence.
PeriodStart (UTC)End (exclusive, UTC)MentionsSubmissionsComments
20172017-01-01 00:00:00 UTC2018-01-01 00:00:00 UTC514
20182018-01-01 00:00:00 UTC2019-01-01 00:00:00 UTC20119
20192019-01-01 00:00:00 UTC2020-01-01 00:00:00 UTC10010
20202020-01-01 00:00:00 UTC2021-01-01 00:00:00 UTC000
20212021-01-01 00:00:00 UTC2022-01-01 00:00:00 UTC000
20222022-01-01 00:00:00 UTC2023-01-01 00:00:00 UTC000
20232023-01-01 00:00:00 UTC2024-01-01 00:00:00 UTC422

From the project README

An excerpt from the repository, fetched Nov 13, 2026.

Read the project’s introduction
Class field declarations for JavaScript Daniel Ehrenberg, Jeff Morrison Stage 4 A guiding example: Custom elements with classes To define a counter widget which increments when clicked, you can define the following with ES2015: class Counter extends HTMLElement { clicked() { this.x++; window.requestAnimationFrame(this.render.bind(this)); } constructor() { super(); this.onclick = this.clicked.bind(this); this.x = 0; } connectedCallback() { this.render(); } render() { this.textContent = this.x.toString(); } } window.customElements.define('num-counter', Counter); Field declarations With the ESnext field declarations proposal, the above example can be written as class Counter extends HTMLElement { x = 0; clicked() { this.x++; window.requestAnimationFrame(this.render.bind(this)); } constructor() { super(); this.onclick = this.clicked.bind(this); } connectedCallback() { this.render(); } render() { this.textContent = this.x.toString(); } } window.customElements.define('num-counter', Counter); In the above example, you can see a field declared with the syntax x = 0. You can also declare a field without an initializer as x. By declaring fields up-front, class definitions become more self-documenting; instances go through fewer state transitions, as declared fields are always present. Private fields The above example has some implementation details exposed to the world that might be better kept internal. Using ESnext private fields and methods, the definition can be refined to: class Counter extends HTMLElement { #x = 0; clicked() { this.#x++; window.requestAnimationFrame(this.render.bind(this)); } constructor() { super(); this.onclick = this.clicked.bind(this); } connectedCallback() { this.render(); } render() { this.textContent = this.#x.toString(); } } window.customElements.define('num-counter', Counter); To make fields private, just give them a name starting with #. By defining things which are not visible outside of the class, ESnext provides stronger encapsulation, ensuring that your classes' users don't accidentally trip themselves up by depending on internals, which may change version to version. Note that ESnext provides private fields only as declared up-front in a field declaration; private fields cannot be created later, ad-hoc, through assigning to them, the way that normal properties can. Major design points Public fields created with Object.defineProperty A public field declarations define fields on instances with the internals of Object.defineProperty (which we refer to in TC39 jargon as [[Define]] semantics), rather than with this.field = value; (referred to as [[Set]] semantics). Here's an example of the impact: class A { set x(value) { console.log(value); } } class B extends A { x = 1; } With the adopted semantics, new B() will result in an object which has a property x with the value 1, and nothing will be written to the console. With the alternate [[Set]] semantics, 1 would be written to the console, and attempts to access the property would lead to a TypeError (because the getter is missing). The choice between [[Set]] and [[Define]] is a design decision contrasting different kinds of expectations of behavior: Expectations that the field will be created as a data property regardless of what the superclass contains, vs expectations that the setter would be called. Following a lengthy discussion, TC39 settled on [[Define]] semantics, finding that it's important to preserve the first expectation. The decision to base public field semantics on Object.defineProperty was based on extensive discussion within TC39 and consultation with the developer community. Unfortunately, the community was rather split, while TC39 came down rather strongly on the side of Object.defineProperty. As a mitigation, the decorators proposal provides the tools to write a decorator to make a public field declaration use [[Set]] semantics. Even if you disagree with the default, the other option is available. (This would be the case regardless of which default TC39 chose.) Public fields are shipping in Chrome 72 with [[Define]] semantics, and this decision on semantics is unlikely to be revisited. Fields without initializers are set to undefined Both public and private field declarations create a field in the instance, whether or not there's an initializer present. If there's no initializer, the field is set to undefined. This differs a bit from certain transpiler implementations, which would just entirely ignore a field declaration which has no initializer. For example, in the following example, new D would result in an object whose y property is undefined, not 1. class C { y = 1; } class D extends C { y; } The semantics of setting fields without initializers to undefined as opposed to erasing them is that field declarations give a reliable basis to ensure that properties are present on objects that are created. This helps programmers keep objects in the same general state, which can make it easy to reason about and, sometimes, more optimizable in implementations. Private syntax Private fields are based on syntax using a #, both when declaring a field and when accessing it. class X { #foo; method() { console.log(this.#foo) } } This syntax tries to be both terse and intuitive, although it's rather different from other programming languages. See the private syntax FAQ for discussion of alternatives considered and the constraints that led to this syntax. There are no private computed property names: #foo is a private identifier, and #[foo] is a syntax error. No backdoor to access private Private fields provide a strong encapsulation boundary: It's impossible to access the private field from outside of the class, unless there is some explicit code to expose it (for example, providing a getter). This differs from JavaScript properties, which support various kinds of reflectio
Read the full README on GitHub

What Hacker News says

5 sources
You get something similar to with static methods and properties on class definitions. In plain ES2017-ish class syntax: class Test { static value = 1; static someStaticMethod = () => { return 5; } } console.log(Test.value) // 1 console.log(Test.someStaticMethod()) // 5 Edit: Sorry, this actually a Stage 3 TC39 feature. Not sure if you can use it with flowtype (I think you can), but you might be able to if you enable babel with stage-3 features: https://github.com/tc39/proposal-class-fields You don't actually need this fancy class syntax anyway. You can just define a class and then do: Test.value = 5 Test.someStaticMethod = () => { return 5; } The class stuff is just sugar over the prototype stuff anyway, and a class is just a constructor function object with a prototype chain.
In Announcing Rust 1.20
I read through the thread, and the proposal sounds crazy to me. 1. Members marked private being accessible from other instances of the same class [1] is counter to every other OO language I have used. What is the prior art here? Why depart from all conventions like this? 2. The # syntax is not extensible. If TC39 decides to add protected, internal, etc. modifiers in the future, what will they look like? We should be looking to TypeScript's private/protected modifiers as a battle tested solution that fits the existing convention set by the "static" modifier. --- [1] https://github.com/tc39/proposal-class-fields/issues/15#issu...
In JavaScript TC39 implementing hashmark private class fields

MAKE YOURSELF AT HOME

A few shortcuts

/
Search the collection
?
Open this guide
Esc
Close dialogs and navigation

Your saved repositories and interests stay in this browser.