Repository navigation
Overcoming this-hesitancy in TC39 #31
Description
A significant objection to this proposal is that this is confusing:
I'm going to go out on a limb and say probably 90% of working JavaScript developers don't understand how this works in method calls at all. And don't care. Don't have to care. Teaching somebody, "well, in this piece of code you have to use double colon and in this piece of code you have to use a period"? Not going to happen. I think that's a social problem and seems to me the technical problem of building a smarter bundler, no matter how hard that is, it's still easier than solving a giant social problem that affects the whole industry.
I figure we can discuss here the potential mitigations of this concern. Below I list some hypotheses that we could pursue further if they sound promising. Any of these hypotheses will likely need to be strongly justified by data to be persuasive.
-
Most code and jobs use TypeScript. TypeScript provides built in protection against accidental misuse of
this. Its error messages can provide just-in-time education that will reach a huge portion of the community relatively quickly and cheaply. EDIT: There are still cases where protection is lost today and perhaps it is worthwhile to tighten up the protection there. I personally have found the existing protections for this-using standalone functions to be quite helpful. -
Presuming that the goal is to make classes tree-shakable for the sake of end users, we can get most of the end user benefit by convincing a relatively few developers to switch to new syntax. So this may not actually be a "whole industry" kind of problem. In particular, I have in mind the maintainers of:
- Broadly-used platforms like Wix, SquareSpace, Wordpress, etc
- High traffic sites like those listed at https://www.semrush.com/website/top/
- High-usage libraries by npm downloads and/or GitHub stars
-
The new syntax will make learning
thiseasier (as long as the educator's goal is to provide understanding and not just find excuses to poke fun at JavaScript). Since it provides a syntax that is as convenient as passing arguments, we can treat it as the canonical/verbose form and others as shorthand:o..fn()callsfnwiththis === oundefined..fn()callsfnwiththis === undefined. In strict mode, you can write this asfn()for shorto..(o.fn)()callso.fnwiththis === o. You can write this aso.fn()for short.- Caveats for arrow functions, bound functions, and non strict mode.
-
There is no way to teach bundlers to delete class methods completely safely. It's literally not possible, given the dynamic nature of JS. The only option without new syntax is to give them permission to take more risk. But this is also a social problem, not a technical one. Convincing people to take more risk, write more tests, enable strict lints, avoid packages that trigger deoptimization, and learn how to debug the failures when prod behavior deviates from dev, is it's own set of social problems.
-
Smarter bundlers are necessarily slower. They must do whole program analysis, just to remove blocks of code that ideally would never have made it into the bundle in the first place. This wastes developer time on an ongoing basis by extending build and CI times.
-
More aggressive bundlers inherently implies increasing the distance between dev and prod semantics, which makes final output harder to reason about. Developers with have to learn the idiosyncrasies of individual bundlers, which is not necessarily transferable knowledge vs learning the behavior of
this, which is highly transferable. -
Companies and libraries whose users could benefit substantially from tree shaking will also invest in educating their devs (and per point 3, I don't think this will be nearly as complicated as people fear).
-
In general, it's better to transfer cost from end users to developers because smaller more concentrated groups are in a much better position to minimize costs, rather than keeping costs diffuse.
-
The way call-this is defined currently, it is possible to offer class-based API without additional wrapper functions for those who do not care about the tree-shaking tradeoff and/or just need backwards compatibility during migration.
-
We can do both (improve the language and improve the bundlers). We want "defense in depth" against dead code. Smarter bundlers and DCE-friendly language features complement each other, rather than compete with each other. Dead code is a big problem so it's better to attack it from multiple angles.
Activity
theScottyJam commentedon Mar 16, 2026
Not trying to derail your general idea, but gonna pick on this point a bit.
Unfortunetally, it usually doesn't.
I don't know if this is because of a technical limitation, backwards compatibility restraints, they just haven't gotten arround to it, or what. But
thisin TypeScript isn't all that type-safe, and I wish it were.ewinslow commentedon Mar 16, 2026
Not derailing at all, this is exactly the kind of discussion that needs to happen. I have no interest in making decisions based on bad arguments or data, so thank you. Will take a look later and figure out the best way to clarify.
ljharb commentedon Mar 16, 2026
While the many vagaries of
thisare indeed not widely known, I think the vast majority of JS devs understand the basic idea that ina.b(),ais thethisvalue inside thea.bmethod.robbiespeed commentedon Mar 16, 2026
This focuses a lot on the advantages the feature brings bundlers. I don't find those arguments as compelling compared to what is gained from the usage and authoring perspective.
As an author this gives me better freedom to separate core class methods from methods that may be more experimental or supplementary. It also opens up opportunity for third parties to author their own supplementary methods.
One can argue that ability roughly exists already by choosing functions with first argument as the recieved rather than
this, but that forces different calling conventions. In turn it makes the decision of what to embed as core methods harder.As a user I get consistent receiver first calling convention for a much larger range of methods. Additionally I highly suspect once language service support of this feature matures I would benefit from improved suggestions/auto complete, since suggestions could be filtered via receiver type.
ewinslow commentedon Mar 16, 2026
@robbiespeed IIUC you're saying you strongly disagree with this particular section of the feedback:
Not only do you think it can happen, but you find it to be much more attractive than the alternatives that people currently have to resort to. If we can have methods that are detached from classes, but still callable on instances in a chainable way, we can experiment more freely with APIs without giving up the familiar calling convention of receiver-method-args, which people are quite comfortable with, contrary to the quoted concern.
I share this intuition. I'd be curious if you (or anyone) can think of a way we might validate or invalidate this intuition with data.
This committee member is complaining that methods written for call-this also result in a different (and even brand new) call syntax, and that during any incremental transition, there will be confusion I.e. sometimes you have to do
o.m(), other times you have to doo..m()instead. I think I'd say the difference betweeno.m()ando..m()is quite minor, means exactly the same thing as the other wheno.m === m, autocomplete can help a ton, other mistakes and typos can be easily caught by TS and are cheaply corrected, and it retains the familiar features of receiver-method-args ordering, autocomplete, and chaining.Any other ideas on how you'd persuade someone worried about developer confusion over
this?ewinslow commentedon Mar 17, 2026
RE #31 (comment):
I tried more some more cases, and got basically what I expected. Actually it appears standalone methods have more protection than class methods because... I don't know. Not clear that's intentional on TS's part...
If I define a this-using function without declaring its
thistype at all, I get complaints:These are the sorts of protections I had in mind. The
setTimeoutissue is indeed unfortunate. Perhaps it can be improved...robbiespeed commentedon Mar 17, 2026
Yes. Any language change will require some amount of learning. Symbols, private fields, optional chaining, are all examples of language features which have successfully changed the way to access properties or call methods.
No one is forcing incremental transition, that'll be a decision for libraries and users. The difference will be fairly clear since in the call-this scenario the function will need to be imported or in scope, just like a symbol.
To that point though I do have concerns about the syntax looking too similar, and would rather we go with a syntax that more clearly distinguishes itself like
o::m().Other than what's already stated, I would try to remind any worried party that developers are quite adaptable (I dare say it's a required trait). And in many cases developers are eager to learn new syntax features.
ewinslow commentedon Mar 18, 2026
Added a 10th point to emphasize that "make bundlers smarter" and "make JS tree-shakeable" are not mutually exclusive strategies. Surely TC39 can improve the language and bundler folk can improve the bundlers for "defense in depth" against dead code.
hax commentedon Mar 26, 2026
I agree with most of the opinions here. In my experience, the confusion around
thislargely stems from some clickbait articles or books that exaggerate the issues withthis, creating a stereotype within the developer community. However, we must also acknowledge that any enhancement of features aroundthis—even with good intentions and excellent design—inevitably expands the body of knowledge aboutthisand increases confusion, at least for a period of time (arrow functions are an example). So I think one way to measure this is how thecall-thisproposal can avoid such problems as much as possible. This might sound somewhat subjective, but we can now use a more objective approach—by observing whether AI becomes confused.Interestingly, I've found that
fn(this: rec, ...args)syntax is very close to the ideal state. Conversely,o..methodis more prone to misinterpretation (as it looks more like a typo to AIs). Of course, the specifics of syntax might be better discussed in #10.