-
Notifications
You must be signed in to change notification settings - Fork 1k
[lit-labs/preact-signals] add abstract class support to SignalWatcher #4487
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
Conversation
🦋 Changeset detectedLatest commit: 6435447 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📊 Tachometer Benchmark ResultsSummarynop-update
render
update
update-reflect
Resultsthis-change
render
update
update-reflect
this-change, tip-of-tree, previous-release
render
update
nop-update
this-change, tip-of-tree, previous-release
render
update
this-change, tip-of-tree, previous-release
render
update
update-reflect
|
|
The size of lit-html.js and lit-core.min.js are as expected. |
| Base: T | ||
| ): T { | ||
| return class SignalWatcher extends Base { | ||
| abstract class SignalWatcher extends Base { |
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.
Hm, should SignalWatcher itself be abstract? I guess probably so.
However technically you could do:
class MyElement extends LitElement {...}
customElements.define('my-element', SignalWatcher(MyElement));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 test below "class returned from signal-watcher should be directly instantiable if non-abstract" seems to suggest this is possible as is.
The tests show an interesting behavior. While I would've expected returning an abstract class here would always result in an abstract class that's non-instantiable, it seems it's still instantiable if the class provided to the mixin is non-abstract?
So then the reason for the addition of abstract in this line is to preserve the abstract behavior if the class provided to the mixin is also abstract. I don't know if that's something we strictly need to preserve, but seems unlikely to be a problem.
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.
customElements.define('my-element', SignalWatcher(MyElement)); is possible and tested by this PR here.
The tests show an interesting behavior. While I would've expected returning an abstract class here would always result in an abstract class that's non-instantiable, it seems it's still instantiable if the class provided to the mixin is non-abstract?
This is accurate. If the passed in class is non-abstract, then the resulting class from the mixin is non-abstract. This is due to the type signature of the mixin, which takes in Base: T, and returns T. So the return type matches the exact class passed in. The mixing-in of SignalWatcher is not reflected in the type system.
So then the reason for the addition of abstract in this line is to preserve the abstract behavior if the class provided to the mixin is also abstract.
This is required in order to pass in an abstract class with abstract members. Otherwise, within the mixin we'd get a type error suggesting that abstract methods/fields need to be implemented. By marking the mixin class abstract, the abstract methods do not need to be implemented by the mixin. We get to have our cake and eat it too, because the abstract mixin class is not reflected in the return type as mentioned above. So a concrete class being passed in can still be directly newed.
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.
Thank you for the excellent explanation!
| Base: T | ||
| ): T { | ||
| return class SignalWatcher extends Base { | ||
| abstract class SignalWatcher extends Base { |
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 test below "class returned from signal-watcher should be directly instantiable if non-abstract" seems to suggest this is possible as is.
The tests show an interesting behavior. While I would've expected returning an abstract class here would always result in an abstract class that's non-instantiable, it seems it's still instantiable if the class provided to the mixin is non-abstract?
So then the reason for the addition of abstract in this line is to preserve the abstract behavior if the class provided to the mixin is also abstract. I don't know if that's something we strictly need to preserve, but seems unlikely to be a problem.
augustjk
left a comment
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.
🎉
Fixes: #4486
For context see the issue. It is now possible to pass an abstract class to the mixin and have it type check.
Test plan
Added type and runtime unit tests to ensure that both concrete and abstract classes are watched correctly.