-
Notifications
You must be signed in to change notification settings - Fork 23
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
add @computed decorator #20
Conversation
6faffd1
to
8bc563d
Compare
@43081j, I've been comparing this to PolymerTS, and I can see why you took a different route. TSC never liked the PolymerTS approach of pretending a method was a property. However, I wonder about how the developer must reference properties upon which the computed getter depends. As far as I understand it (@aomarks can correct me if I'm wrong), you can't guarantee the state of those properties if you reference them through @computed(['dependencyOne', 'dependencyTwo'])
get computedTwo() { return this.dependencyOne + this.dependencyTwo; } I don't believe we can guarantee that |
This implementation basically comes from my faint memory of a conversation with @justinfagnani last year, in which we both agreed a computed property is pretty much just a getter anyway. So I figured if we can at least implement the simple/common use case like one, it'll make things much nicer/smoother. As for your question, i discussed this briefly with @rictic before making the PR and we saw no reason the properties wouldn't have updated yet. Though if anyone can correct us, i don't mind rethinking this a little. Edit: ah actually i wasn't thinking very well. with multiple dependencies, maybe it will be hit twice so we need some care around there. still need more feedback here from who i already cc'd. |
@43081j actually, after re-reading things, I think the properties would be initialized properly as long as they're listed in the array of properties sent to the decorator. |
a966516
to
ed3cf49
Compare
@aomarks @justinfagnani i changed this back to an array rather than spread... because TS doesn't like having the options parameter after a spread parameter. |
@43081j ah, that makes sense, but it's a bit unfortunate. Given that, I'd say that we keep spread for |
c71ccd4
to
71d48e5
Compare
@justinfagnani i agree, ive changed it back and removed the options. |
71d48e5
to
5ca58d9
Compare
rebased this onto master now too so it should be ready to merge if we're all settled on implementation design |
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.
LGTM. Let's wait for @justinfagnani before merging.
src/decorators.ts
Outdated
@@ -100,6 +105,29 @@ export function observe(targets: string|string[]) { | |||
} | |||
} | |||
|
|||
/** | |||
* A TypeScript accessor decorator factory that causes the decorated accessor to | |||
* be called when a property changes. `targets` is either a single property |
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.
The caller should never pass a list of property names though, which I think this could be read as implying. I'd say something like "The arguments are the paths of data dependencies of the computer property, as described [here](https://www.polymer-project.org/2.0/docs/devguide/observers#define-a-computed-property)"
src/decorators.ts
Outdated
@@ -100,6 +105,29 @@ export function observe(targets: string|string[]) { | |||
} | |||
} | |||
|
|||
/** | |||
* A TypeScript accessor decorator factory that causes the decorated accessor to |
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.
I'd add some detail that the decorator should be applied to a getter, and that there should be no associated setter (I believe it'll be overwritten). Also, I think it's worth mentioning that unlike a plain getter, this will only be called when dependencies change, not on access, which might cause unexpected results for side-effects like logging.
src/decorators.ts
Outdated
export function computed<T = any>(...targets: (keyof T)[]) { | ||
return (proto: any, propName: string, descriptor: PropertyDescriptor): void => { | ||
const targetString = targets.join(','); | ||
const propNameCased = propName[0].toUpperCase() + propName.slice(1); |
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.
I don't think it's really necessary to name-case the property. Just add a separator before it below, like _
.
src/decorators.ts
Outdated
const propNameCased = propName[0].toUpperCase() + propName.slice(1); | ||
const fnName = `__compute${propNameCased}`; | ||
|
||
proto.constructor.prototype[fnName] = descriptor.get; |
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.
proto.constructor.prototype
should be unnecessary, just use proto
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.
i had trouble using proto
before but will give it another go
src/decorators.ts
Outdated
const propNameCased = propName[0].toUpperCase() + propName.slice(1); | ||
const fnName = `__compute${propNameCased}`; | ||
|
||
proto.constructor.prototype[fnName] = descriptor.get; |
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.
I think we should really use Object.defineProperty
here, in case there's a name collision up the prototype chain.
src/decorators.ts
Outdated
descriptor.get = undefined; | ||
|
||
createProperty(proto, propName, { | ||
computed: `${fnName}(${targetString})` |
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.
I think it's fine to inline the targetString
expression here.
We found a Contributor License Agreement for you (the sender of this pull request), but were unable to find agreements for the commit author(s). If you authored these, maybe you used a different email address in the git commits than was used to sign the CLA (login here to double check)? If these were authored by someone else, then they will need to sign a CLA as well, and confirm that they're okay with these being contributed to Google. |
e041178
to
43104c9
Compare
CLAs look good, thanks! |
5f3df95
to
bb1971d
Compare
bb1971d
to
46ea331
Compare
@justinfagnani hows this? added the changes you proposed |
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.
LGTM, thanks @43081j!
This is a computed decorator (fixes #17).
The way its expected to be used is as follows:
It only supports simple parameters!
What this means is that splices, wildcards, length, etc. will not work. Anything which would require us to pass something into the computation function is not supported here. For that, you should create a
@property
withcomputed
set manually.To me this seems like a sacrifice worth giving so simple computations can remain as what they really are: getters. If you think otherwise, speak up and we can discuss how to support complex ones too.