Skip to content

refactor(ui): remove as unknown as Parameters<typeof X>[0] casts — restore structural type-checking #2494

@alexey-pelykh

Description

@alexey-pelykh

Problem

ui/src/ui/app.ts uses as unknown as Parameters<typeof X>[0] 21 times. ui/src/ui/app-lifecycle.ts uses the same pattern 10+ times. The double-cast through unknown is regular TypeScript syntax (not a @ts-ignore directive, so lint doesn't flag it), but it deliberately erases the structural type check that would otherwise verify the source class satisfies the target interface.

This pattern is the structural enabler of the gateway-disconnect regression (see Issue A). Every time upstream adds a required field to LifecycleHost / GatewayHost / ToolStreamHost / PollingHost / ChatHost / SettingsHost / ScrollHost / CompactionHost, and the fork-side RemoteClawApp class doesn't get the corresponding initializer, TypeScript cannot detect the divergence. The field is undefined at runtime; the bug surfaces only when consumed.

Fix strategy

Two-phase:

Phase 1: Replace as unknown as Parameters<typeof X>[0] with as Parameters<typeof X>[0] (drop the unknown) throughout ui/src/ui/app.ts and ui/src/ui/app-lifecycle.ts. This will surface a cascade of type errors — each error is a real structural divergence the unknown was hiding.

Phase 2: Resolve each surfaced error by adding missing fields to RemoteClawApp (or whichever source class). No new casts allowed — each error is the type system telling the truth.

Scope

grep -rnE 'as unknown as Parameters' ui/src/ui/

Known hotspots:

  • ui/src/ui/app.ts — 21 occurrences (lines 318, 329, 333, 337, 342, 346, 351, 358, 368, 372, +11 others)
  • ui/src/ui/app-lifecycle.ts — 10+ occurrences (lines 48, 50, 51, 52, 58, 60, 62, 65, 70, 76, …)

Acceptance criteria

  • Zero occurrences of as unknown as Parameters<typeof X>[0] in ui/src/ui/ (grep returns nothing).
  • pnpm tsgo passes.
  • pnpm check (format + typecheck + lint) passes.
  • pnpm test passes.
  • Chat dashboard still connects to gateway (regression test of Issue A fix).
  • No new @ts-ignore / @ts-expect-error / as unknown as casts added to silence the surfaced errors — every error resolved at the source class.

Edge cases to document

Some casts may be legitimate (e.g., crossing a type-system boundary the type system cannot model — manual deserialization, test partial objects). For those cases, document inline:

// TYPE-ERASURE: {reason, e.g., "Zod-validated payload, runtime assertion below"}
const x = raw as unknown as T;

Expected count after refactor: 0 legitimate cases in ui/src/ui/app.ts / app-lifecycle.ts. All 31+ current casts are bridging class to host interface — the fix is to make the class satisfy the interface, not to erase the check.

Commit message

refactor(ui): remove as unknown as Parameters<typeof X>[0] casts — restore structural type-checking

Impact

Prevents the regression class described in Issue A's investigation (partial-sync dropping class-side changes) from recurring invisibly.

References

  • Related: Issue A (dependent fix — must land first)
  • Related: Issue D (smoke test adds runtime defense-in-depth)
  • Related: Issue H (audit catches missing fields surfaced by this refactor)

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions