Description
This issue is tracking problems that come up due to allowing normal JavaScript property lookup to work with computed properties.
Context
We are in the process of landing a change that will allow you to use normal property lookups to get properties defined as computed properties in Ember.
In practice, what this means is that, in most cases (more later) you will be able to drop .get('key')
and replace it with .key
.
However, there are a handful of small historical issues that could make the transition a little more messy. If you landed on this issue, chances are you encountered a warning from Ember that you encountered one of these historical issues.
Computed Properties used to be directly on the prototype
Until Ember 3.0, internal implementation details of Ember caused computed properties to be directly present on objects, so if you didn't use .get()
, you would see the internal "Ember descriptor" representing the computed property (a private API).
This internal implementation detail was never intended to be a public (or even intimate) API.
This internal implementation detail has now changed and the (still private) "descriptor" object has been relocated to the object's "meta" (also a private API). This allows us to make normal property lookup work, and in general, very few people were relying on the presence of this internal descriptor.
If you encountered an error related to this problem, you are probably using an addon that relies on this now-defunct private implementation detail, and your best bet is to follow the instructions in the error message (and ask for help in #dev-ember).
Ember Proxies are tricky
Secondly, since the new behavior relies on using defineProperty
, it does not allow users of Ember proxies to stop using .get()
.
Previously, since all Ember objects required you to use .get()
to access properties, you could uniformly use .get()
in your code and never need to worry about whether an object was an Ember proxy.
However, now that you can use normal property access for computed properties, Ember proxies are special. If an object is implemented using an Ember proxy, it should advertise itself as requiring .get()
to access its proxied properties.
This is analogous to other libraries in the JavaScript ecosystem, such as immutable.js
, that expose an API that is different from normal JS property access patterns.
Since proxies are relatively rare in Ember compared to property access on other Ember objects, emberjs/rfcs#281 (now merged) felt that the cost of losing consistent property access (everyone types .get()
everywhere, so you don't need to know when an object is "special") was worth the benefit of normally not needing to use .get()
.
During the transition, we are worried that people will accidentally migrate some .get()
s on Ember proxies to regular property access, so we added an assertion to the code that fires if:
- you use a normal property get on an Ember proxy
- the property is not present on the object itself
unknownProperty
returns a value other than undefined
(this development-mode assertion is implemented using ES2015 proxies, which are not yet present on all supported browsers, but will cover the browsers people typically use for development)
For the most part, this avoids the vast majority of mistakes but it does introduce a possible problem with code that "probes" objects to discover whether they implement protocols.
Here's what I mean by "probing" code:
if (obj.toJSON) {
return obj.toJSON();
}
A more resilient way to write this code would be:
if ('toJSON' in obj && typeof obj.toJSON === 'function') {
return obj.toJSON();
}
Unfortunately, apps are not always in control of the probing code (which could come from test frameworks, generic serialization logic, etc.).
Notably, "probing" code is only a problem if:
- an Ember proxy finds its way to the probing code
- the property is not present on the object itself (not true about
toString
, which is usually a real property of the Ember proxy itself) - and this is the important part:
unknownProperty
returns something other thanundefined
It's definitely possible that an Ember proxy could return some object for every possible property, and that some probing code checks for a protocol that's not usually present on the wrapper object (like toString
), which would trigger the assertion inappropriately.
This problem comes up now because probing code previously would only look at properties of the wrapper proxy object, but now, in order to provide good errors for people accidentally removing .get()
that they shouldn't have, normal property accesses on Ember proxies become sensitive to the behavior of unknownProperty
.
Again, the correct mental model going forward is that these objects (most notably the values returned from relationships in Ember Data) are now special objects and require a non-standard API, but the process of migration introduces this ambiguity.
If you got this error and didn't expect it, please reply to this issue with information about what happened so we have help improve the error message with more further advice and workarounds.