Skip to content

Bug: Setting statics in a class via its derived class is allowed #29381

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JoshuaKGoldberg opened this issue Jan 11, 2019 · 4 comments
Closed
Labels
Question An issue which isn't directly actionable in code

Comments

@JoshuaKGoldberg
Copy link
Contributor

JoshuaKGoldberg commented Jan 11, 2019

TypeScript Version: 3.3.0-dev

Search Terms: accessing private statics in a class via its derived class is allowed

Code

class FooBase {
    public static someProperty = "unset";

    testBase(): void {
        Foo.someProperty = "set";

        // Unexpectedly "unset"
        console.log(FooBase.someProperty);
   }
}

class Foo extends FooBase { }

new FooBase().testBase();

Expected behavior:

Setting someProperty should set it to the value, right?

Actual behavior:

Setting a static member of a parent class via its derived class does not set the property of the parent class. It sets it on the derived class instead.

Playground Link: http://www.typescriptlang.org/play/#src=class%20FooBase%20%7B%0D%0A%20%20%20%20public%20static%20someProperty%20%3D%20%22unset%22%3B%0D%0A%0D%0A%20%20%20%20testBase()%3A%20void%20%7B%0D%0A%20%20%20%20%20%20%20%20Foo.someProperty%20%3D%20%22set%22%3B%0D%0A%0D%0A%20%20%20%20%20%20%20%20%2F%2F%20Unexpectedly%20%22unset%22%0D%0A%20%20%20%20%20%20%20%20console.log(FooBase.someProperty)%3B%0D%0A%20%20%20%7D%0D%0A%7D%0D%0A%0D%0Aclass%20Foo%20extends%20FooBase%20%7B%20%7D%0D%0A%0D%0Anew%20FooBase().testBase()%3B

Related Issues:

#8624 tracks accessing private statics via derived classes. This issue is for setting any statics via derived classes. Filing a separate issue here because it's a bit larger and more breaking of a change.

Would this be too breaking of a change to accept in TypeScript? IMO yes but I figured I'd file anyway. 😊 I'm conflicted...

@weswigham
Copy link
Member

Why would setting the property on the static subclass change the property in the base class? They share a prototypical relationship - meaning if you assign to FooBase only, changes will be reflected on reads of the prop thru Foo, but once you set it directly on Foo you shadow the prop that you were reading from FooBase, and changes do not affect FooBase.

@weswigham weswigham added Domain: Quick Fixes Editor-provided fixes, often called code actions. Question An issue which isn't directly actionable in code and removed Domain: Quick Fixes Editor-provided fixes, often called code actions. labels Jan 11, 2019
@JoshuaKGoldberg
Copy link
Contributor Author

@weswigham yes, and the shadowing of the prop is unexpected, right? If you run the OP's code (I added a call to the method at the end) it logs "unset" instead of "set", which is surprising. If Foo declared its own someProperty that would be one thing, but it doesn't, so the user would expect there to be only one property. The Foo.someProperty = "set" is making a new property when one isn't expected to exist.

@weswigham
Copy link
Member

yes, and the shadowing of the prop is unexpected, right? If you run the OP's code (I added a call to the method at the end) it logs "unset" instead of "set", which is surprising.

It might be surprising, but that's just how prototypical inheritance works (across all of JS!). Take this more javascripty example:

function Foo (shouldSet) {
  if (shouldSet)
    this.item = "set";
}
Foo.prototype.item = "unset";
var f1 = new Foo(true);
f1.item; // is "set"
var f2 = new Foo(false);
f1.item; // is "unset", but if it worked like you suggest, would be "set" - all instances would share the property!

@fatcerberus
Copy link

Yes, this is just how prototype inheritance works. I would find it more surprising if setting a property on the child affected the parent, because then there would be no way to shadow things on sub-objects!

If you still want the write to go through the base class, make it an accessor (getter/setter).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

4 participants