Skip to content

SSE onSseEvent callback is typed as StreamEvent<unknown> — TResponse not forwarded through Options #3463

@bilby91

Description

@bilby91

Description

When using onSseEvent in the SDK function for an SSE endpoint, the callback parameter is typed as StreamEvent<unknown> instead of StreamEvent<ActualResponseType>. This makes it impossible to use the event data without casting.

Root Cause

The generated Options type in sdk.gen.ts only forwards two generic parameters (TData and ThrowOnError) to the underlying client Options type, but not TResponse:

// Generated sdk.gen.ts
export type Options<
  TData extends TDataShape = TDataShape,
  ThrowOnError extends boolean = boolean,
> = Options2<TData, ThrowOnError> & { ... };

The client's Options type (client/types.gen.ts) has a third TResponse parameter that flows into RequestOptions<TResponse>ServerSentEventsOptions<TResponse>StreamEvent<TResponse>. Since TResponse defaults to unknown, the callback receives StreamEvent<unknown>.

The SSE SDK function also doesn't pass the response type:

// Generated - missing TResponse
export const subscribeToEventStream = <ThrowOnError extends boolean = false>(
  options: Options<SubscribeToEventStreamData, ThrowOnError>,
) => ...

Expected Behavior

The Options type should forward TResponse, and SSE SDK functions should pass the response type:

export type Options<
  TData extends TDataShape = TDataShape,
  ThrowOnError extends boolean = boolean,
  TResponse = unknown,
> = Options2<TData, ThrowOnError, TResponse> & { ... };

export const subscribeToEventStream = <ThrowOnError extends boolean = false>(
  options: Options<SubscribeToEventStreamData, ThrowOnError, SubscribeToEventStreamResponse>,
) => ...

This way onSseEvent receives StreamEvent<SubscribeToEventStreamResponse> and event.data is the properly typed discriminated union.

Notes

  • The Nuxt client path in operationOptionsType already resolves symbolResponseType with role: "response" and passes it — the non-Nuxt path does not.
  • For SSE endpoints specifically, using role: "response" (the unwrapped Response type) rather than role: "responses" (the { 200: ... } wrapper) is important so that StreamEvent.data matches the actual runtime shape of SSE events.

Reproduction

Any SSE endpoint with a typed response schema. The onSseEvent callback will show event: StreamEvent<unknown> in the IDE instead of the expected typed event.

Versions

  • @hey-api/openapi-ts: 0.92.3
  • @hey-api/client-fetch

Metadata

Metadata

Assignees

No one assigned

    Labels

    bug 🔥Broken or incorrect behavior.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions