Skip to content

fix(beacon-node): restructure unknownBlock test to fix tsgo overload resolution - #9488

Merged
nflaig merged 1 commit into
ChainSafe:te/fix_block_input_sync_metrics_logsfrom
lodekeeper:fix/tsgo-emitter-overload
Jun 9, 2026
Merged

fix(beacon-node): restructure unknownBlock test to fix tsgo overload resolution#9488
nflaig merged 1 commit into
ChainSafe:te/fix_block_input_sync_metrics_logsfrom
lodekeeper:fix/tsgo-emitter-overload

Conversation

@lodekeeper

Copy link
Copy Markdown
Contributor

Summary

  • Restructures the "downloads the block and retries payload import when EL reports block not in fork choice" test in unknownBlock.test.ts so its processBlock mock no longer needs a let emitter!: ChainEventEmitter definite-assignment assertion plus a destructuring-assignment dance.
  • That pattern caused tsgo's overload resolution to widen emitter's inferred type and fall back to the EventEmitter base overload (expecting unique symbol event names) for the subsequent emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, ...) site — surfaced as the TS2769 check-types failure on this PR's CI.

Diagnosis

Failing job: Type Checks (24)packages/beacon-node check-types.

test/unit/sync/unknownBlock.test.ts(1027,20): error TS2769: No overload matches this call.
  Argument of type 'ChainEvent.unknownEnvelopeBlockRoot' is not assignable to parameter of type 'unique symbol'.

The data shape is fine — slot: 0 is already included at line 1024, and the e2e at unknownBlockSync.test.ts:236 also includes slot: headSlot. The 5 other emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, ...) sites in the same file (lines 762/836/926/1079/1129) all typecheck fine because they use plain const {emitter} = setupPayloadSyncTest(...) rather than let ! + destructuring-assignment.

Fix

Declare processBlock = vi.fn() upfront, destructure emitter as a const, then attach processBlock.mockImplementation(...) after setupPayloadSyncTest returns. This matches the pattern used by every other test in the file and removes the pattern that confused tsgo.

Safe because no event fires between setupPayloadSyncTest and the next emitter.emit(...)processBlock is never invoked before its implementation is attached.

Test plan

  • CI: Type Checks (24) passes (no TS2769 on unknownBlock.test.ts).
  • CI: the "downloads the block and retries payload import when EL reports block not in fork choice" unit test still passes (still asserts processBlock called once, processExecutionPayload called twice).

🤖 Generated with Claude Code

…resolution

The test "downloads the block and retries payload import when EL reports
block not in fork choice" used a `let emitter!: ChainEventEmitter`
definite-assignment assertion + destructuring-assignment to satisfy a
closure dependency where `processBlock`'s mock impl referenced `emitter`
before `setupPayloadSyncTest` returned.

tsgo's overload resolution then widened the variable's type after the
destructuring assignment and failed to pick the StrictEventEmitter
overload for the subsequent `emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, ...)`
call, falling back to the EventEmitter base overload whose first arg
expects `unique symbol`:

  test/unit/sync/unknownBlock.test.ts(1027,20): error TS2769:
  No overload matches this call.
  Argument of type 'ChainEvent.unknownEnvelopeBlockRoot' is not
  assignable to parameter of type 'unique symbol'.

Restructure to: declare `processBlock = vi.fn()` without impl, destructure
`emitter` as a `const`, then attach the mock implementation after
`setupPayloadSyncTest` returns. This matches the pattern used by the 5
other passing emit sites in the same file. Safe because no event is
emitted between `setupPayloadSyncTest` and the subsequent emit, so
`processBlock` is not invoked before `mockImplementation` is set.

🤖 Generated with AI assistance
@lodekeeper
lodekeeper requested a review from a team as a code owner June 9, 2026 11:42

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors a unit test in unknownBlock.test.ts to improve readability and avoid definite assignment assertions. Specifically, it separates the initialization of processBlock and emitter by mocking the implementation of processBlock after emitter has been destructured from setupPayloadSyncTest. There are no review comments provided, and I have no additional feedback to offer.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@nflaig
nflaig merged commit fda72f7 into ChainSafe:te/fix_block_input_sync_metrics_logs Jun 9, 2026
1 check passed
@lodekeeper

Copy link
Copy Markdown
Contributor Author

Thanks for the review pass — appreciated. (Noted on the consumer-version sunset.)

nflaig pushed a commit that referenced this pull request Jun 9, 2026
…verload (#9491)

## Summary

Follow-up to #9488 — the restructure removed the `let !` + destructuring
pattern but tsgo's overload-resolution miss persists at the same emit
site because `emitter` is also captured by a sibling closure (the
`processBlock` mock body that emits `routes.events.EventType.block`).
Same `TS2769` error reproduces on the current head:

```
test/unit/sync/unknownBlock.test.ts(1027,20): error TS2769: No overload matches this call.
  Argument of type 'ChainEvent.unknownEnvelopeBlockRoot' is not assignable to parameter of type 'unique symbol'.
```

## Fix

Cast `emitter` at the failing site to `ChainEventEmitter` to re-anchor
the `StrictEventEmitter` overload for `ChainEvent.X` keys. Minimal
change — single line cast at line 1023, with a comment explaining why.
The 5 other `emitter.emit(ChainEvent.unknownEnvelopeBlockRoot, ...)`
sites in the file remain untouched because they aren't reached through a
sibling closure capture.

The previous restructure (now in the base branch) is still worth keeping
— it removed an unnecessary indirection — but it wasn't sufficient on
its own.

## Test plan

- [ ] CI: `Type Checks (24)` passes (no TS2769 on
`unknownBlock.test.ts`).
- [ ] CI: unit tests for the "downloads the block and retries payload
import when EL reports block not in fork choice" test still pass — emit
semantics unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: lodekeeper <lodekeeper@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants