Repository navigation
Conversation
In JavaScript, `ReleaseEmitter = EventEmitter` over `const EventEmitter =
require("events")` was refused with SC1090 ("a runtime-provided class value
as an untyped value"), and `class Channel extends require("events")` failed
on the require call as a value. Bundled npm code does both, for example a
small semaphore that aliases EventEmitter and constructs its release channel
through the alias.
- The events module binding and the EventEmitter or stream class itself
turn untyped as the runtime's untyped constructor that constructor
functions already use (scr_dyn_builtin_class.c). `new` through it builds
an emitter object with Node's listener API, and `===` against the class
compares identity.
- `x instanceof EventEmitter` on an untyped value also recognizes a boxed
compiled emitter (an instance of a program class extending EventEmitter),
which the prototype test alone answered false for. The same holds through
an untyped binding narrowed to the class.
- `class X extends require("events")` extends the runtime emitter, as
through a require binding.
- A typed array of the class built from such values (`[EventEmitter]`) is
refused with SC1090 rather than failing its class check at run time.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Comment on lines
+176
to
+184
| const once = (expr: IrExpr, name: string): IrExpr => { | ||
| if (isSafeToRepeat(expr)) return expr; | ||
| const local = lowerer.declareHiddenLocal(name, DYN); | ||
| stmts.push({ kind: "varDecl", localId: local.id, init: expr, loc }); | ||
| return { kind: "varRef", localId: local.id, type: DYN, loc }; | ||
| }; | ||
| const tested = once(value, "%instanceValue"); | ||
| const ctor = callee ? once(callee, "%instanceCallee") : null; | ||
| const compiled = classInstanceOf(lowerer, tested, info, loc); |
Contributor
This was referenced Oct 20, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #1537 (
opt/require-decl-storage); review that first.Summary
In JavaScript,
require("events")is the EventEmitter class, and bundled npm code uses it as a value. A small semaphore does this in its bundled chunk:The assignment was refused with SC1090 ("a runtime-provided class value ('EventEmitter') as an untyped value"), and
class Channel extends require("events") {}failed on the require call as a value.#1512 gave JavaScript an untyped EventEmitter constructor (
scr_dyn_builtin_class.c) for constructor functions, and already used it for a destructuredEventEmitterbinding orevents.EventEmitter. This change uses the same constructor wherever the class turns untyped:const EventEmitter = require("events")) and a direct reference to EventEmitter or a stream class become that constructor when they flow into an untyped slot.newthrough it builds an emitter object with Node's listener API, asnewon a constructor function inheriting from it does. Only the class itself converts: a slot of the class's type can hold a program subclass, so such slots keep the refusal.untyped === EventEmittercompares identity with that constructor.untyped instanceof EventEmitternow also recognizes a boxed compiled emitter, an instance of a program class extending EventEmitter; the prototype test alone answered false for those. The same applies through an untyped binding the checker narrows to the class.extends require("events"). The base is the runtime emitter, as through a require binding.[EventEmitter]stored as a typed array of the class is refused with SC1090. Before, the destructured binding already produced the untyped constructor there, and the array then failed its class check at run time with a TypeError.The stream classes follow the same path where they turn untyped; constructing over them keeps #1512's run-time
SC2020until #1533 lands.require("stream")itself is unchanged.Evidence
6571-events-module-class-value.cjs: the events module binding stored, aliased, constructed through and compared; SC1090 on main.6572-extends-require-events.cjs:class extends require("events")used as a value; failed on main.6573-semaphore-release-emitter.cjs: a semaphore whose constructor builds its release emitter through an aliased require binding and listens and emits on it; SC1090 on main.2595-bundled-chunk-emitter: a minified esbuild__commonJSchunk with the mixedvarstatement, a sibling chunk require, a class extending the binding and an alias; fails on main.6453and6454testuntyped instanceof EventEmitter, which now also checks for a boxed compiled emitter. Bench geomean +0.13%, self-hosted compiler −0.26%, both within tolerance.test:ts7 --baselines-onlyare clean; the self-hosting IR build has no diagnostics.Not covered
onfollowed by an untypedemiton the same native emitter aborts at run time (pre-existing, also in TypeScript); it is being fixed separately.listenerCount,eventNamesand similar on a boxed compiled emitter are not routed yet.🤖 Generated with Claude Code