Skip to content

refactor(agents): remaining type cast cleanup #63

@zrosenbauer

Description

@zrosenbauer

Summary

Audit of as type casts in packages/agents/src identified casts that are necessary but worth tracking for future cleanup when TypeScript or dependencies provide better alternatives.

Remaining casts to track

1. Zod v4 private API access — output.ts:59

const def = (schema as unknown as Record<string, unknown>)["_zod"] as
  | { def: { element?: ZodType } }
  | undefined;

Accesses Zod's private _zod.def.element to extract the element schema from z.array(). Zod v4 does not expose a public API for this. The guard fails safely (throws a descriptive error instructing users to use Output.array() explicitly), but the cast reaches into internals that may change in a Zod major version.

Action: Replace with a public Zod API if one becomes available. Tracked with a TODO comment in the code.

2. onFinish union cast — flow-agent.ts (generate + stream)

const configOnFinish = config.onFinish as
  | ((event: { input: TInput; result: FlowAgentGenerateResult<TOutput>; duration: number }) => void | Promise<void>)
  | undefined;

FlowAgentConfig is a discriminated union (WithOutput | WithoutOutput) where onFinish has different result types (FlowAgentGenerateResult<TOutput> vs FlowAgentGenerateResult<string>). TypeScript can't call a union of contravariant functions, and discriminant narrowing doesn't help because the result variable's type doesn't change with the narrowing. The cast is correct because the implementation signature uses TOutput = any.

Action: Could be eliminated by adding a shared onFinish to FlowAgentConfigBase with a base event type that both variants extend. Low priority — the cast is well-documented and safe.

3. PromiseLike vs Promise in stream results

BaseStreamResult fields (output, usage, finishReason) are typed as PromiseLike<T> (from AI SDK), which doesn't have .catch(). All suppression sites use .then(undefined, noop) with oxlint-disable comments. This is correct but noisy.

Action: Consider whether BaseStreamResult fields should be Promise<T> instead of PromiseLike<T>, or add a utility like suppressRejection(promiseLike).

Already fixed in this audit

  • Removed unnecessary as Record<string, Tool> / as SubAgents casts on empty object defaults (agent.ts)
  • Removed unnecessary as Logger narrowing replaced with log! (agent.ts)
  • Removed vestigial as unknown as StreamResult & { ok: true } and as unknown as BaseGenerateResult & { ok: true } double-casts in factory.ts — TS narrowing works correctly after !result.ok guard
  • Removed OnFinishHook type alias that was only used for the cast (cast still needed but inlined with explanation)
  • Fixed .catch() on PromiseLike in test files
  • Added explanatory comment for Zod internals access

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions