fix(core): invoke structured Signal listeners with runtime args before bound args

Previously `Signal._addListener` applied bound arguments first and runtime
signal args last, producing `method(...boundArgs, ...signalArgs)`. This made
the event object's position shift with the number of bound arguments and
diverged from the DOM / Cocos `(event, customEventData)` convention.

Flip the order so the listener method receives runtime args first and bound
args last: `method(...signalArgs, ...boundArgs)`. The event object now always
sits at index 0, making migrated Cocos scripts with `(event, customData)`
signatures work without rewrites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cptbtptpbcptdtptp
2026-04-21 17:31:35 +08:00
parent 80bdb53c39
commit 536ebdfd84

View File

@@ -20,9 +20,11 @@ export class Signal<T extends any[] = []> {
on(fn: (...args: T) => void, target?: any): void;
/**
* Add a structured binding listener. Structured bindings support clone remapping.
* The target method will be invoked as `method(...signalArgs, ...args)` —
* runtime signal arguments come first, bound arguments are appended.
* @param target - The target component
* @param methodName - The method name to invoke on the target
* @param args - Pre-resolved arguments
* @param args - Pre-resolved arguments appended after the runtime signal arguments
*/
on(target: Component, methodName: string, ...args: any[]): void;
on(fnOrTarget: ((...args: T) => void) | Component, targetOrMethodName?: any, ...args: any[]): void {
@@ -37,9 +39,11 @@ export class Signal<T extends any[] = []> {
once(fn: (...args: T) => void, target?: any): void;
/**
* Add a one-time structured binding listener.
* The target method will be invoked as `method(...signalArgs, ...args)` —
* runtime signal arguments come first, bound arguments are appended.
* @param target - The target component
* @param methodName - The method name to invoke on the target
* @param args - Pre-resolved arguments
* @param args - Pre-resolved arguments appended after the runtime signal arguments
*/
once(target: Component, methodName: string, ...args: any[]): void;
once(fnOrTarget: ((...args: T) => void) | Component, targetOrMethodName?: any, ...args: any[]): void {
@@ -171,7 +175,7 @@ export class Signal<T extends any[] = []> {
const methodName = targetOrMethodName as string;
const fn =
args.length > 0
? (...signalArgs: any[]) => (target as any)[methodName](...args, ...signalArgs)
? (...signalArgs: any[]) => (target as any)[methodName](...signalArgs, ...args)
: (...signalArgs: any[]) => (target as any)[methodName](...signalArgs);
this._listeners.push({
fn: fn as (...args: T) => void,