Summary
A user method that overrides a native base-class method is silently bypassed: the call dispatches straight to the native implementation and the override never runs. super.<method>() from inside such an override also doesn't reach the base.
Confirmed on a clean origin/main build (0960415ad).
Repro
import { EventEmitter } from "node:events";
class Bus extends EventEmitter {
emit(ev: string, ...args: any[]): boolean {
console.log("override ran:", ev);
return super.emit(ev, ...args);
}
}
const b = new Bus();
b.on("ping", (x: number) => console.log("listener got:", x));
console.log("emit returned:", b.emit("ping", 42));
|
output |
| node 26 |
override ran: ping / listener got: 42 / emit returned: true |
perry main |
listener got: 42 / emit returned: true |
The override ran: line is missing — b.emit(...) went directly to the native EventEmitter.emit, skipping the user's method entirely. No error, no warning.
Why it matters
Subclassing EventEmitter and overriding emit/on to add logging, filtering, or instrumentation is an extremely common Node pattern. Today it silently does nothing, which is a wrong answer rather than a crash — the worst failure mode.
Scope
Surfaced while fixing #6301 (class X extends EventTarget inheriting nothing). That issue's fix routes EventTarget methods through the class chain and places them last on the lookup path so a subclass override wins. This issue is the mirror problem for native bases generally: the override is not consulted at all.
Two things to establish:
- Which native base classes are affected (
EventEmitter confirmed; check EventTarget, Error, Array, Promise, stream classes).
- Whether the dispatch bypass and the
super.<method>() failure are one root cause or two.
Note super.<method>() into a native base was independently observed as broken during #6301 and scoped out of that PR as orthogonal — this is its tracking issue.
Summary
A user method that overrides a native base-class method is silently bypassed: the call dispatches straight to the native implementation and the override never runs.
super.<method>()from inside such an override also doesn't reach the base.Confirmed on a clean
origin/mainbuild (0960415ad).Repro
override ran: ping/listener got: 42/emit returned: truemainlistener got: 42/emit returned: trueThe
override ran:line is missing —b.emit(...)went directly to the nativeEventEmitter.emit, skipping the user's method entirely. No error, no warning.Why it matters
Subclassing
EventEmitterand overridingemit/onto add logging, filtering, or instrumentation is an extremely common Node pattern. Today it silently does nothing, which is a wrong answer rather than a crash — the worst failure mode.Scope
Surfaced while fixing #6301 (
class X extends EventTargetinheriting nothing). That issue's fix routesEventTargetmethods through the class chain and places them last on the lookup path so a subclass override wins. This issue is the mirror problem for native bases generally: the override is not consulted at all.Two things to establish:
EventEmitterconfirmed; checkEventTarget,Error,Array,Promise, stream classes).super.<method>()failure are one root cause or two.Note
super.<method>()into a native base was independently observed as broken during #6301 and scoped out of that PR as orthogonal — this is its tracking issue.