@@ -22,10 +22,45 @@ type eventHandlerType<Type> = Type extends TypedEvent<infer X> ? X : never;
22
22
* - The values are of typed 'TypedEvent'.
23
23
*
24
24
* @param Base - The class which will be extended with event subscription methods.
25
+ * @param path - The path to the TypedEvent member in the Base class. This is optional and only needed
26
+ * if the events are not directly on the Base class.
25
27
* @returns A subclass of Base with event subscription methods added.
26
28
*/
27
29
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
28
- export function AddEvents < TBase extends Constructor , U > ( Base : TBase ) {
30
+ export function AddEvents < TBase extends Constructor , U > ( Base : TBase , path ?: string ) {
31
+ /**
32
+ * Get the event object from the given instance. If a path is given, the object will be traversed
33
+ * using the path.
34
+ *
35
+ * @param instance - The instance to get the event object from.
36
+ * @returns The event object.
37
+ */
38
+ function getEventObject ( instance : any ) {
39
+ if ( ! path ) {
40
+ return instance ;
41
+ }
42
+ return path . split ( '.' ) . reduce ( ( obj , key ) => ( obj ? obj [ key ] : undefined ) , instance ) ;
43
+ }
44
+
45
+ /**
46
+ * Get the event object from the given instance. If a path is given, the object will be traversed
47
+ * using the path. If the event object is not found, an error will be thrown.
48
+ *
49
+ * @param instance - The instance to get the event object from.
50
+ * @param eventName - The name of the event to get.
51
+ * @returns The event.
52
+ */
53
+ function getEvent < K extends keyof U > ( instance : any , eventName : K ) {
54
+ const eventsObject = getEventObject ( instance ) ;
55
+ const event = eventsObject ?. [ eventName ] ;
56
+ if ( ! event ) {
57
+ // Construct the full path for the error message
58
+ const fullPath = path ? `${ path } .${ String ( eventName ) } ` : String ( eventName ) ;
59
+ throw new Error ( `Event "${ fullPath } " is not defined` ) ;
60
+ }
61
+ return event ;
62
+ }
63
+
29
64
// eslint-disable-next-line jsdoc/require-jsdoc
30
65
return class WithEvents extends Base {
31
66
/**
@@ -37,7 +72,7 @@ export function AddEvents<TBase extends Constructor, U>(Base: TBase) {
37
72
on < K extends keyof U , E extends eventHandlerType < U [ K ] > > ( eventName : K , handler : E ) {
38
73
// Even though we bypass type safety in the call (casting this as any), we've enforced it in the
39
74
// method signature above, so it's still safe.
40
- ( this as any ) [ eventName ] . on ( handler ) ;
75
+ getEvent ( this , eventName ) . on ( handler ) ;
41
76
}
42
77
43
78
/**
@@ -49,7 +84,7 @@ export function AddEvents<TBase extends Constructor, U>(Base: TBase) {
49
84
* @param handler - The handler.
50
85
*/
51
86
once < K extends keyof U , E extends eventHandlerType < U [ K ] > > ( eventName : K , handler : E ) {
52
- ( this as any ) [ eventName ] . once ( handler ) ;
87
+ getEvent ( this , eventName ) . once ( handler ) ;
53
88
}
54
89
55
90
/**
@@ -61,7 +96,7 @@ export function AddEvents<TBase extends Constructor, U>(Base: TBase) {
61
96
* @param handler - The handler.
62
97
*/
63
98
off < K extends keyof U , E extends eventHandlerType < U [ K ] > > ( eventName : K , handler : E ) {
64
- ( this as any ) [ eventName ] . off ( handler ) ;
99
+ getEvent ( this , eventName ) . off ( handler ) ;
65
100
}
66
101
} ;
67
102
}
0 commit comments