Skip to content

fix(stream): drop @ts-expect-error via intersection-cast (forward-compat with @types/node 25.7.0)#155

Merged
oorabona merged 4 commits into
masterfrom
fix/ts-expect-error-stream-cancel
May 14, 2026
Merged

fix(stream): drop @ts-expect-error via intersection-cast (forward-compat with @types/node 25.7.0)#155
oorabona merged 4 commits into
masterfrom
fix/ts-expect-error-stream-cancel

Conversation

@oorabona

@oorabona oorabona commented May 14, 2026

Copy link
Copy Markdown
Owner

Summary

Unblocks the daily Refresh Lockfile workflow, which has been producing one "build failed" PR per day since 2026-05-06 (#143-#145, #149-#152 — 7 stale PRs).

Root cause

@types/node@25.7.0 added cancel?: TransformerCancelCallback to the Transformer interface in stream/web.d.ts. The catalog spec ^25.5.0 accepts it during unfrozen install, so:

  • Committed pnpm-lock.yaml pins @types/node@25.6.0 (no cancel) → the @ts-expect-error directives in src/wasm/stream.ts are necessary and silent.
  • Refresh-lockfile workflow runs pnpm install --no-frozen-lockfile → resolves to 25.7.0 (has cancel) → directives become unused → TS2578 → build step fails → workflow opens a "build failed" PR every day.

The same regression will hit master CI the next time @types/node lands at >= 25.7.0 in the lockfile (Dependabot bump, manual upgrade, etc.).

Fix

Drop the directives entirely. Cast the inline Transformer literal as Transformer<Uint8Array, Uint8Array> & { cancel?: (reason: unknown) => void | PromiseLike<void> } at both new TransformStream({...}) construction sites in src/wasm/stream.ts. The intersection adds the missing cancel?: on @types/node@25.6.0 (where the base Transformer has no cancel) and is structurally compatible on 25.7.0+ (where the base has the typed cancel?:). The cast signature mirrors the Streams spec / TransformerCancelCallback so the call contract stays visible.

Net result: zero @ts-expect-error / @ts-ignore / biome-ignore in the file. Biome's lint/suspicious/noTsIgnore rule has nothing to act on. The pre-commit hook is a no-op for this file.

Typings state Before this PR (@ts-expect-error) After this PR (intersection cast)
cancel absent (25.6.0) suppresses error structurally adds the property
cancel present (25.7.0+) TS2578 fails build structurally compatible — no friction

Why not switch to @ts-ignore?

The intuitive fix — swap @ts-expect-error@ts-ignore — fails silently. The project's pre-commit hook (simple-git-hookspnpm exec nano-staged) runs biome check --write on staged .ts files. Biome v2's lint/suspicious/noTsIgnore rule has a safe fix (auto-applied with --write) that rewrites @ts-ignore@ts-expect-error as a text replace — even when the directive is suppressed by a biome-ignore pragma on the line above. Three earlier rounds of this PR tried the swap and watched the rewrite undo it between git add and git commit. The intersection-cast sidesteps the rule entirely.

Regression guard

Adds test/wasm/stream-directive-hygiene.test.ts. The test reads src/wasm/stream.ts and asserts that no // @ts-expect-error or // @ts-ignore directive is present. If a future change re-introduces either, the test fails on every PR — not only on the next lockfile refresh.

Verification

  • pnpm type-check clean against both @types/node@25.6.0 (lockfile pin) and @types/node@25.7.0 (refresh-lockfile resolution).
  • biome check src/wasm/stream.ts clean (with and without --write).
  • New hygiene test passes locally.

Test plan

Follow-ups (not blocking)

@types/node 25.7.0 added `cancel?: TransformerCancelCallback` to the
`Transformer` interface in stream/web.d.ts. On the older 25.6.0 that
ships with the committed pnpm-lock.yaml the property is absent, so
`@ts-expect-error` was suppressing a real error. On the daily
refresh-lockfile workflow's regenerated lockfile (which picks 25.7.0
within the catalog `^25.5.0` range) the directive becomes unused and
trips TS2578, blocking the workflow's build step and causing 9 stale
"chore(deps): refresh lockfile (build failed)" PRs (#143-#145, #149-#152).

Switch to `@ts-ignore`:

- Old typings (cancel absent): suppresses the assignment error (same
  behavior as before).
- New typings (cancel present): no-op, no warning. Forward-compatible
  with future @types/node releases.

Reproduction:
  pnpm add -D -E @types/node@25.7.0 && pnpm build  # passes
  pnpm add -D -E @types/node@25.6.0 && pnpm build  # passes
Copilot AI review requested due to automatic review settings May 14, 2026 13:45

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

The PR intends to unblock the daily Refresh Lockfile workflow that started failing after @types/node@25.7.0 added cancel?: TransformerCancelCallback to Transformer, which makes the existing @ts-expect-error directives in src/wasm/stream.ts unused (TS2578). The stated fix is to swap those two directives for @ts-ignore so they remain harmless on both old and new typings, and to expand the inline rationale comment.

Changes:

  • Expand the comment above the cancel handler in createXz() to explain the typings situation.
  • Cross-reference that explanation from the comment above the cancel handler in createUnxz().
  • (Per description) switch the two @ts-expect-error directives to @ts-ignore — not actually present in the diff.
Comments suppressed due to low confidence (2)

src/wasm/stream.ts:132

  • Same issue as line 72: directive is still @ts-expect-error rather than @ts-ignore as the PR description claims, so this will produce TS2578 once @types/node 25.7.0+ is resolved.
    // @ts-expect-error cancel is a valid Transformer method per Streams spec

src/wasm/stream.ts:131

  • "rationale on @ts-expect-error vs @ts-expect-error" compares the directive to itself. This should reference @ts-ignore vs @ts-expect-error to match the intended fix described in the PR.
    /* cancel() frees WASM resources when the readable side is cancelled.
       See createXz() for the rationale on @ts-expect-error vs @ts-expect-error. */

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wasm/stream.ts Outdated
Comment thread src/wasm/stream.ts Outdated
…rior commit)

Prior commit 50eea34 updated the rationale comments but left the directives
unchanged, so @types/node 25.7.0 would still trip TS2578.

- src/wasm/stream.ts L72, L132: directive @ts-expect-error@ts-ignore
- comments: replaced nonsensical "@ts-expect-error (not @ts-expect-error)"
  self-comparison with "@ts-ignore (not @ts-expect-error)"
@oorabona oorabona requested a review from Copilot May 14, 2026 14:06

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comments suppressed due to low confidence (2)

src/wasm/stream.ts:132

  • Same issue as on line 72: this directive is still @ts-expect-error rather than @ts-ignore, contradicting both the PR description and the new comment at line 130-131 which references the (non-existent) switch. On @types/node@25.7.0+, where cancel is now declared on Transformer, this directive becomes unused and triggers TS2578, failing the build.
    // @ts-expect-error cancel is a valid Transformer method per Streams spec

src/wasm/stream.ts:131

  • Same self-referential mistake as in the createXz comment: "rationale on @ts-expect-error vs @ts-expect-error" compares the directive to itself. This should read @ts-ignore vs @ts-expect-error per the PR description.
    /* cancel() frees WASM resources when the readable side is cancelled.
       See createXz() for the rationale on `@ts-expect-error` vs `@ts-expect-error`. */

Comment thread src/wasm/stream.ts Outdated
Comment thread src/wasm/stream.ts Outdated
oorabona added 2 commits May 14, 2026 16:13
…rewrite

Rounds 1 and 2 silently regressed because Biome's recommended
`suspicious/noTsIgnore` rule, run via `biome check --write` from the
nano-staged pre-commit hook, auto-rewrites `@ts-ignore` back to
`@ts-expect-error`. Net result: the directive swap never reached HEAD
even though local edits applied correctly.

Adds `// biome-ignore lint/suspicious/noTsIgnore: ...` pragma above each
`// @ts-ignore` directive so the hook leaves them untouched. The
`@ts-ignore` choice is deliberate — `@ts-expect-error` would fire TS2578
the moment `@types/node` 25.7.0 (which adds `Transformer.cancel`) lands
in the lockfile, exactly the breakage the daily Refresh Lockfile workflow
keeps surfacing.

Verified: `pnpm type-check` clean (current lockfile), `biome check
src/wasm/stream.ts` clean (via rtk proxy to bypass RTK rewrite).
…ward-compat)

`@types/node@25.7.0` added `cancel?: TransformerCancelCallback` to the
`Transformer` interface in `node:stream/web`. The previous `@ts-expect-error`
directives suppressing the missing-`cancel` error on older typings would
become "unused" (TS2578) on 25.7.0+, which is exactly what the daily
`Refresh Lockfile` workflow has been opening "build failed" PRs about
since 2026-05-06.

Switching the directive to `@ts-ignore` is not viable: the pre-commit hook
runs `biome check --write`, and Biome v2's `lint/suspicious/noTsIgnore`
safe-fix rewrites `@ts-ignore` back to `@ts-expect-error` as a text
replace — even inside `biome-ignore` suppression pragmas. Verified
empirically.

Instead, cast the inline `Transformer` literal as `Transformer<Uint8Array,
Uint8Array> & { cancel?: (reason: unknown) => void | PromiseLike<void> }`.
The intersection adds `cancel?:` on `@types/node@25.6.0` (where it's
absent) and is structurally compatible on 25.7.0+ (where it's present).
Zero `@ts-ignore`/`@ts-expect-error`/`biome-ignore` directives in the
file. The `cancel` signature matches the Streams spec (`reason: unknown
=> void | PromiseLike<void>`), so the call contract stays visible.

Adds `test/wasm/stream-directive-hygiene.test.ts` as a deterministic
regression detector: if a future change re-introduces either directive
in `src/wasm/stream.ts`, the test fails on every PR — not just on the
next lockfile refresh.

Verified locally against both `@types/node@25.6.0` (lockfile) and
`@types/node@25.7.0`: `tsc --noEmit` clean both ways, biome check clean
(with and without `--write`), new test passes.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

Comment thread src/wasm/stream.ts
@oorabona oorabona changed the title fix(stream): tolerate @types/node Transformer.cancel availability fix(stream): drop @ts-expect-error via intersection-cast (forward-compat with @types/node 25.7.0) May 14, 2026
@oorabona oorabona merged commit bc0bf0f into master May 14, 2026
23 checks passed
@oorabona oorabona deleted the fix/ts-expect-error-stream-cancel branch May 14, 2026 14:47
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