-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Fix event types and .once this binding #9130
Conversation
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.
Looks good, see comments
Could yuo add a test for the once handler, so we cover it?
src/Observable.ts
Outdated
K extends string | number | symbol = string, | ||
E = any | ||
> = Record<K, TEventCallback<E>>; | ||
type EventRegistryObject<E = any> = { |
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.
should E extend Record<string, any>
?
Then K
should always be string and no symbol etc.
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.
should E extend Record<string, any>?
I tried it but it doesn't seem to help to restrict the key type to string
. In turn it would force EventSpec
to be a Record
it self, which is okay, I just didn't do it to avoid in turn affecting maybe other types as well. But if you're okay with it, I can try.
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.
Okay I've tried it and it still gives the error about string | number | symbol
.
Using Object.entries
fixes the issue, probably for (key in obj)
is hardcoded to have type string | number | symbol
because you're looping over all the keys, even non-own properties.
However, extends Record<string, any>
brought to CommonMethods<CanvasEvents>
giving error because CanvasEvents
cannot be assigned to Record<string, any>
. It works only with type
, not interface
. But we need interface
for declaration merging.
See:
- Index signature is missing in type (only on interfaces, not on type alias) microsoft/TypeScript#15300
- https://www.typescriptlang.org/play?#code/C4TwDgpgBAogbhAdsAShA5gSwM7AE4gA8MUEAHsEgCbZRoDGA9nlYbnpougDRSICuAWwBGEPAD5xUALxQA3lADaAaSicoAawghGAM1gBdAFx8hovFAC+AKGuhIsBMgDKkejLoQmLNvk49TETFxa05KPF0AQ3poeCRgVy8AJnlrKHSoSJMk6xs7cGgAQQ845DQsdiJShLcQ+2gAIRKnVAwcfCqWxPok8SA
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.
Does this solve the issue?
I didn't fully understand the interface limitation. Didn't read it thoroughly.
This is when I say ohh TS... why???
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 am ok using Object.entries
Very annoying the for in loop wrong static types
Just for discussion, do you know of a perf diff between them?
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.
Another question about declaration merging
Why not pass an augmented type to the class? That was my intention when I wrote it. I didn't think of declaration merging because I don't use it so much.
I am used to generics
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.
So if i understand correctly using types is better for maintainers because TS is simpler to use and interfaces for devs/consumers for declarstiin merging but that takes a toll from maintaining?
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 am ok using Object.entries Very annoying the for in loop wrong static types Just for discussion, do you know of a perf diff between them?
Or we just @ts-expect-error
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.
Does this solve the issue?
Not really, because at that point you can do const value: EventSpec2 = { a: 2, b: 3 }
, e.g. add extra properties without error. And that's exactly why TS gives the error in my example, because technically you're saying that something with "well-defined properties" is assignable to a type where "any string index can be used", which is unsafe.
Just for discussion, do you know of a perf diff between them?
There is definitely an overhead in using functions, but not significant. Also not relevant for an object of event handlers, whose number of entries is only a few and the code is not in a critical hot path (rendering).
Why not pass an augmented type to the class? That was my intention when I wrote it. I didn't think of declaration merging because I don't use it so much. I am used to generics
Declaration merging is different than generics. Generic is for a specific instance, a declaration merging extends an existing interface wherever it's used. It can also significantly harder for the lib to maintain good generics. For instance the following would need to be fixed as the EventSpec
generic is currently not used:
Here you see you shortcuted to use CanvasEvents
directly and it worked. With generics you would need to ensure that all the generics from fabric.Canvas
down to Observable
work correctly. But if you fix everything and make it work, it's definitely a doable alternative. At that point not sure if there are still other use cases where declaration merging for canvas/object events might be helpful.
Also, what do you think about a TS test suite? #8392 |
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 noticed this
Or on master:
Lines 28 to 31 in d2c7b89
on<K extends string, E>( | |
eventName: K, | |
handler: TEventCallback<E> | |
): VoidFunction; |
Lines 73 to 76 in d2c7b89
once<K extends string, E>( | |
eventName: K, | |
handler: TEventCallback<E> | |
): VoidFunction; |
Why is it there? I am to blame... Maybe the prev type needed it.
Seems that it should be removed, right?
I don't know but I noticed it to be weird indeed. I can remove it if you want, no idea why it was there though. |
lets remove the wierd artifact |
Done |
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 am good with this PR, it also fixes autocomplete of .on({ ... })
Declaration merging, interface vs. type, poses a problem that we should decide about soon.
@asturur do you accept it for 6.0?
on, once, off
event registering. It was particularly not working for registering with an object of event handlers, since the type ofEventRegistryObject
wasRecord<K, TEventCallback<E>>
, meaning that every event has the same callback type, whereas each event has its own.obj.once("event", function() { console.log(this) })
wherethis === undefined
, whereasobj.on("event", function() { console.log(this) })
would correctly havethis === object