feat(observable): enhance event callback type specificity (#10720)

This commit is contained in:
Arthur
2025-03-16 17:48:51 +01:00
committed by GitHub
parent 852011c4f9
commit e2f9687e72
2 changed files with 13 additions and 4 deletions

View File

@@ -1,4 +1,4 @@
import { Optional } from '../../utils/typescript-utils'; import { Optional, EndsWith } from '../../utils/typescript-utils';
/** /**
* Base event data. * Base event data.
@@ -160,7 +160,7 @@ export class Observable {
* `this` context when the callback is called. Falsy values will be not be * `this` context when the callback is called. Falsy values will be not be
* bound. * bound.
*/ */
public on(eventName: string, callback: (data: EventData) => void, thisArg?: any): void { public on<S extends string>(eventName: S, callback: (data: EndsWith<S, 'Change', PropertyChangeData, EventData>) => void, thisArg?: any): void {
this.addEventListener(eventName, callback, thisArg); this.addEventListener(eventName, callback, thisArg);
} }
@@ -175,7 +175,7 @@ export class Observable {
* `this` context when the callback is called. Falsy values will be not be * `this` context when the callback is called. Falsy values will be not be
* bound. * bound.
*/ */
public once(eventName: string, callback: (data: EventData) => void, thisArg?: any): void { public once<S extends string>(eventName: S, callback: (data: EndsWith<S, 'Change', PropertyChangeData, EventData>) => void, thisArg?: any): void {
this.addEventListener(eventName, callback, thisArg, true); this.addEventListener(eventName, callback, thisArg, true);
} }
@@ -188,7 +188,7 @@ export class Observable {
* @param thisArg An optional parameter which, when set, will be used to * @param thisArg An optional parameter which, when set, will be used to
* refine search of the correct event listener to be removed. * refine search of the correct event listener to be removed.
*/ */
public off(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void { public off<S extends string>(eventName: S, callback?: (data: EndsWith<S, 'Change', PropertyChangeData, EventData>) => void, thisArg?: any): void {
this.removeEventListener(eventName, callback, thisArg); this.removeEventListener(eventName, callback, thisArg);
} }

View File

@@ -4,3 +4,12 @@
* // returns: { eventName: string; object?: Observable } * // returns: { eventName: string; object?: Observable }
*/ */
export type Optional<T, K extends keyof T> = Omit<T, K> & { [P in K]?: T[P] }; export type Optional<T, K extends keyof T> = Omit<T, K> & { [P in K]?: T[P] };
/**
* Determines if a string type ends with a specified suffix.
* @example type IsChangeEvent = EndsWith<"propertyNameChange", "Change", true, false>
* // returns: true
* @example type IsChangeEvent = EndsWith<"someEvent", "Change", true, false>
* // returns: false
*/
export type EndsWith<T extends string, S extends string, PassType = true, FailType = false> = T extends `${infer _}${S}` ? PassType : FailType;