Size / Priority
- Size: M — 50+ call sites; not a single PR.
- Category: C.2 Simplifications & DRY (type-safety cleanup).
- Risk: medium —
as casts often hide latent type bugs; uncovering them may require type fixes.
Affected files
- ~50+ sites across the codebase where
as unknown as T or as any is used to coerce a value into a typed shape.
Background
as unknown as T is the TypeScript escape hatch for casts the compiler can't verify. It's currently used liberally:
- Wire-message → typed shape —
(msg as WireMessage) after JSON.parse.
- ActorRef → specific subclass —
(ref as RemoteActorRef).
- Untyped config value → typed settings —
(config.get('x') as MySettings).
- Generic-type juggling —
(crdt as Crdt<any>).
The framework has ~50 of these. Each one is an unchecked cast — a latent bug if the runtime shape doesn't match the type.
Audit pattern from Plan-Doc/A.5.1: malicious snapshot store returns events in wrong order → recovery corrupts state silently. Cast-debt makes such issues invisible at compile time.
Target
Per-cast cleanup with category-specific solutions:
Category 1: Wire-message decoding (JSON.parse result)
// Before:
const msg = JSON.parse(raw) as WireMessage;
// After: typed validator at the boundary
const msg = decodeWireMessage(raw); // returns WireMessage, throws on shape mismatch
Use a typed JSON-validator (Zod, valibot, or hand-rolled discriminator-checks). Validate once at the wire boundary; downstream code sees typed values.
Category 2: ActorRef subtype-cast
// Before:
const local = ref as LocalActorRef;
// After: type-guard
if (!isLocalActorRef(ref)) throw new Error('expected LocalActorRef');
// local is now correctly narrowed.
Category 3: Untyped config → typed settings
Covered by #255 (resolveSettings<S>).
Category 4: Generic-juggling
Often these are honestly necessary (variance issues, etc.). Document each with a comment explaining why.
Implementation
This is a multi-PR cleanup, not a single change. Per-PR:
- Categorise a batch of casts.
- Apply the appropriate fix per category.
- Test each.
Recommend ~10 casts per PR, ~5-6 PRs total.
Integration / risk
- Behavioural changes possible: if a cast was hiding a runtime bug, the fix may surface a new error path. Treat each surfaced bug as a separate issue.
- TS strictness gain: removing casts forces type accuracy at the boundary; the rest of the code becomes more type-safe.
Test plan
- Per-PR regression — full test suite per change.
- New error paths exposed — investigate; either fix the underlying issue or document.
- TS strict mode — codebase should compile cleanly without the casts.
Acceptance criteria
Pre-implementation note
This is a sustained effort. Consider tracking the cast-count over time as a code-health metric.
Size / Priority
ascasts often hide latent type bugs; uncovering them may require type fixes.Affected files
as unknown as Toras anyis used to coerce a value into a typed shape.Background
as unknown as Tis the TypeScript escape hatch for casts the compiler can't verify. It's currently used liberally:(msg as WireMessage)afterJSON.parse.(ref as RemoteActorRef).(config.get('x') as MySettings).(crdt as Crdt<any>).The framework has ~50 of these. Each one is an unchecked cast — a latent bug if the runtime shape doesn't match the type.
Audit pattern from
Plan-Doc/A.5.1: malicious snapshot store returns events in wrong order → recovery corrupts state silently. Cast-debt makes such issues invisible at compile time.Target
Per-cast cleanup with category-specific solutions:
Category 1: Wire-message decoding (JSON.parse result)
Use a typed JSON-validator (Zod, valibot, or hand-rolled discriminator-checks). Validate once at the wire boundary; downstream code sees typed values.
Category 2: ActorRef subtype-cast
Category 3: Untyped config → typed settings
Covered by #255 (
resolveSettings<S>).Category 4: Generic-juggling
Often these are honestly necessary (variance issues, etc.). Document each with a comment explaining why.
Implementation
This is a multi-PR cleanup, not a single change. Per-PR:
Recommend ~10 casts per PR, ~5-6 PRs total.
Integration / risk
Test plan
Acceptance criteria
as unknown as T+as anysites.Pre-implementation note
This is a sustained effort. Consider tracking the cast-count over time as a code-health metric.