Skip to content
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

Merged
merged 7 commits into from
Aug 2, 2023
Merged

Conversation

jiayihu
Copy link
Contributor

@jiayihu jiayihu commented Jul 28, 2023

  • Made event type definitions interfaces to allow again declaration merging
  • Fixed the types for the on, once, off event registering. It was particularly not working for registering with an object of event handlers, since the type of EventRegistryObject was Record<K, TEventCallback<E>>, meaning that every event has the same callback type, whereas each event has its own.
  • Fixed a bug with obj.once("event", function() { console.log(this) }) where this === undefined, whereas obj.on("event", function() { console.log(this) }) would correctly have this === object

@jiayihu jiayihu changed the title Fix event types Fix event types and .once this binding Jul 28, 2023
Copy link
Contributor

@ShaMan123 ShaMan123 left a 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?

K extends string | number | symbol = string,
E = any
> = Record<K, TEventCallback<E>>;
type EventRegistryObject<E = any> = {
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

@jiayihu jiayihu Jul 29, 2023

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:

Copy link
Contributor

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???

Copy link
Contributor

@ShaMan123 ShaMan123 Jul 29, 2023

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?

Copy link
Contributor

@ShaMan123 ShaMan123 Jul 29, 2023

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

Copy link
Contributor

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?

Copy link
Contributor

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

Copy link
Contributor Author

@jiayihu jiayihu Jul 30, 2023

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?

Screenshot 2023-07-30 at 12 43 07

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:

Screenshot 2023-07-30 at 12 45 29

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.

src/Observable.ts Show resolved Hide resolved
src/Observable.ts Outdated Show resolved Hide resolved
src/Observable.ts Outdated Show resolved Hide resolved
@ShaMan123
Copy link
Contributor

Also, what do you think about a TS test suite? #8392

ShaMan123
ShaMan123 previously approved these changes Jul 29, 2023
Copy link
Contributor

@ShaMan123 ShaMan123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this

https://github.com/jiayihu/fabric.js/blob/35a27e37606db22fb857b05891d55e422cb99a8d/src/Observable.ts#L27-L30

https://github.com/jiayihu/fabric.js/blob/35a27e37606db22fb857b05891d55e422cb99a8d/src/Observable.ts#L70-L73

Or on master:

on<K extends string, E>(
eventName: K,
handler: TEventCallback<E>
): VoidFunction;

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?

@jiayihu
Copy link
Contributor Author

jiayihu commented Jul 30, 2023

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.

@ShaMan123
Copy link
Contributor

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

@jiayihu
Copy link
Contributor Author

jiayihu commented Jul 30, 2023

Done

ShaMan123
ShaMan123 previously approved these changes Aug 1, 2023
Copy link
Contributor

@ShaMan123 ShaMan123 left a 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?

asturur
asturur previously approved these changes Aug 2, 2023
@asturur asturur dismissed stale reviews from ShaMan123 and themself via 58a460f August 2, 2023 09:10
@asturur asturur merged commit e0a2d6c into fabricjs:master Aug 2, 2023
8 of 9 checks passed
@jiayihu jiayihu deleted the ts/events branch August 7, 2023 11:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants