-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Incorrect/overly strong this
inference for POJOs
#8191
Comments
I'd like to add that TC39 had the opportunity to make the desugaring from The problem (in that context) that I was worried about, is that existing code expects to take a POJO, enumerate over its properties, and do something with that enumeration. The class pattern is the most common example, but options bags that can contain arbitrary functions is common as well. I was worried that if refactoring from A similar situation is happening here in my opinion. Because there are some use-cases where inferring The meaning of "method in a POJO" is simply too ambiguous to try to guess that the user probably meant "this object is a singleton with methods", and that approach creates too much friction to using concise methods in the large to justify the cognitive overhead, in my opinion. |
On the other hand, we've gotten the request to type |
@RyanCavanaugh Are you sure that the eleventy million times are all cases where the object literal is the correct In my JavaScript experience, the set of cases where |
|
@mhegazy what we want is |
Actually that isn't quite right: |
But more generally, I don't think this change in behavior is what people have in mind much of the time when they ask for Or even more generally, it should be the job of jQuery, Ember.Object, Backbone.Model, React.createClass, etc to provide the |
the counter example is: var x = {
foo() { .. },
bar() { this.| /* why no foo here? */ }
}; |
That may be a theoretical counterexample but how often does it happen in practice relative to the React.createClass and jQuery.ajax patterns? At least for me, cases where I'm writing a POJO with methods that I intend to use as-is is in the minority relative to cases where This change means that people writing Backbone.Model.extend, React.createClass or Ember.Object.extend (as well as jQuery.ajax, new Benchmark(options), etc.) will not be able to use concise syntax, or they will have to say It will also produce this WAT that is similar to yours: let options = {
url: "microsoft.com"
};
Object.assign(options, {
invoke() { /* why doesn't this.url work? */ }
}); |
Well you'll be able to specify the type of |
@DanielRosenwasser I don't know what criteria or rationale you are using to discuss this topic, but it would be helpful for me to understand:
It may well be that I'm in a bubble, and (2) is much less common in the real world than (1), but I'd really appreciate some insight here. |
My feeling is (1) tends to be more common but that people using (2) haven't complained because @mhegazy mentioned above that we could accommodate both scenarios accurately (with contextual types). I think so too, but I think that the signature could get kind of hairy (#8252 (comment) ?). I'll have to look at it when I get back. |
@mhegazy and I hacked in the intersection of the contextual type and the object literal type. This works for the small examples here and doesn't seem to break any other tests (except 1, see below). Basically, you get the intersection of the contextual type and the object literal's type as the this type. However, if the contextual type of the object literal is It's in the branch smarter-object-literal-this-contextual-type-WIP. It causes a Salsa (Javascript) test to fail when assigning a function to prototype. I'll figure out why that happens and clean it up for a PR. |
#8356 fixes this issue, although it does so in a weird way, by intersecting the contextual type with the object literal's type. I'm not sure this is the right approach even though it works for the examples discussed here. |
Sorry to butt in on all the fun... Most of the real world examples I have run across fall into @wycats (1) category as well. The (2) category does seem rare, mostly because of the I don't think intersecting the contextual type with the object literal's type is weird, I think in most cases that is exactly what is being expressed. If/when it is merged, I will want to see if it makes some of the composition problems I have a bit easier (or if the inferred |
For now #8356 is a more conservative fix that says
Previously (1) and (2) wouldn't set the This doesn't make the compiler smarter about interface Arguments {
init?: () => void;
willDestroy?: () => void;
[propName: string]: any;
}
declare function extends(arguments: Arguments);
extends({
init() {
// this: any here -- Arguments.init doesn't declare a `this` parameter.
}
foo() {
// this: any here -- the string indexer contextually types 'foo': any
// and this means that foo's this: any.
}
} And the context-free object literal typing stills works. We'll continue thinking about whether intersecting the object literal type with the contextual type is a good idea in general. var o = {
prop = 12
bar() {
this./*should see 'prop' and 'bar' here*/
}
} |
@wycats, and @chancancode can you give |
@mhegazy I can confirm that it's been fixed! Thank you! |
Since microsoft/TypeScript#8191 is now fixed
See #14141. |
TypeScript Version:
nightly (1.9.0-dev.20160419)
Code
Expected behavior:
this
should be inferred asany
Actual behavior:
this
is inferred to be{ init(): void; }
This changed somewhere between
[email protected]
and[email protected]
. I believe it is unsafe to assume anything about thethis
on the "methods" on a POJO, as there are several common patterns in JavaScript that breaks this assumption (using POJOs as an "options" or "extensions" dictionary).The text was updated successfully, but these errors were encountered: