-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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
Support this
pseudo-params in classes
#1775
Conversation
b83b0d8
to
b026c34
Compare
10: static absSM(this: Class<this> & Class<V>): string; | ||
^ property `virtualSM` of statics of V. Property not found in | ||
6: declare class X { | ||
^ statics of X |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This loc
misses X
. Is it even the correct line? See line 36 above for the nonstatic analogue.
this
pseudo-params in classes, and enforce subtyping on structural class staticsthis
pseudo-params in classes
I'm incorporating this branch into a project currently, and I have an interface method that I want to bind with return type |
method(this: this & I): number { | ||
return this.x + this.anotherMethod(); | ||
} | ||
static staticMethod(this: Class<I> & Class<this>): number { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Under this: Class<this> & Class<I>
, the this
-fixing process leaves a tvar flowing to SpeculativeMatchT instead of a concrete type. When the flow finally does trigger, the speculative testing setup has already unwound, and the error leaks.
Swapping the intersection members like they're configured here allows the method to be found on Class<I>
without ever touching Class<this>
, hence the absence of an error in the current configuration.
Quick and dirty hypothesis check--the following eliminates the spurious error:
(*Above the (head_l, SpeculativeMatchT) case (calls `speculative_match head_l u`)*)
| (ClassT instance, UseT (_, SpeculativeMatchT _)) -> (
let reason = prefix_reason "statics of " (reason_of_t instance) in
let tvar = mk_tvar cx reason in
rec_flow cx trace (instance, GetPropT(reason, (reason, "statics"), tvar));
rec_flow cx trace (tvar, ReposLowerT (reason, u))
)
| (InstanceT _, UseT (k, ClassT instance)) ->
let reason = prefix_reason "statics of " (reason_of_t instance) in
let tvar = mk_tvar cx reason in
rec_flow cx trace (instance, GetPropT(reason, (reason, "statics"), tvar));
rec_flow cx trace (l, UseT(k,tvar))
Find a cleaner fix and remove the TODO.
It looks like concretize_lower_parts
and friends is failing, causing this problem.
@popham Apologies for not getting the chance to look at this earlier. My sense is that unless we try doing this feature for all functions (i.e., recognize them as potential open methods that could appear in method bags and be "mixin"d a bunch of times before eventually being called), we'd not know what the entire range of issues are. Internally the type system already carries the type of At this point we simply don't know what the general case might look like, and it is an often-enough requested feature that we think we'll be working on it at some point in the near future. Locking down part of the design before we've had the chance to think through the general case seems risky. Overall the class use-case is simpler, and simpler solutions might exist. We already know a lot about I think the general case is much more interesting, and gets to the heart of what's going on with open methods. So I propose we hold off on this PR. How do you feel about implementing abstract methods (as outlined above) instead? Does that not satisfy all your use cases? |
Fair enough. I expect some OO-dogmatist heads to explode at the idea of an abstract static method. I'll do it initially behind the (With Flow, I have to pause once in a while and remind myself which code makes it through to the run-time build. I figured that having the abstract method syntax aligned with the eventual pseudo-param syntax on functions would make it easier to remember that these "methods" disappear, although the absence of a function body serves as a pretty good reminder. I'll reclassify my fixation on the run-time build as a personal problem :).) |
This patch adds support for a this pseudo-param on class methods (static and non-static). With the syntax
someMethod(this: this & I,...
users can impose requirement beyond "is subtype of the enclosing class," the current behavior. Some goofiness becomes admissible too, e.g. someMethod(this: {})--just don't be goofy (maybe a warning is warranted if the pseudo-param's type is not a subtype of the enclosing class).I have a use case where the value in this extension lies in chaining of static methods, not non-statics, so I've extended class subtyping to structural class statics. I feel like I'm on solid ground after a chat with @jeffmo and @avikchaudhuri on IRC, although during that discussion I forgot to contrast ES5 and Node inheritance against ES6 inheritance. I recall in the past that @samwgoldman and I disagreed on whether class statics should subtype.Node's inheritance (util.inherit) and the ad hoc inheritance of ES5 ((PR #1847)SomeClass.prototype = Object.create(ParentClass.prototype, {...})
withoutSomeClass.__proto__ = ParentClass
) do not chain class statics. Subtyping statics is biased toward ES6 inheritance, whereas the status quo is biased toward Node and ad hoc inheritance.Closes #807, #1369,
#1704, #1705A lot of the material here is part of PR #1823. Some has been factored out into PR #1847. Pulls there and a rebase here would remove a lot of noise.
The patch is a work-in-progress.There's a few false negatives in the tests.Also, generic class declarations are broken. I want to poll IRC on how I should approach that aspect.