Skip to content

Commit adde78c

Browse files
authored
fix(openapi): preserve event iterator return type in JsonifiedClient (#1797)
`JsonifiedClient` was losing an event iterator's return type: a contract with `eventIterator(z.number(), z.number())` produced `AsyncIteratorObject<number, any, unknown>` instead of a properly typed iterator. `AsyncIteratorClass`'s `return(value?: any)` signature made TypeScript infer `TReturn` as `any` when matched against `AsyncIteratorObject<infer U, infer V>`, since inference works off actual method signatures rather than the `implements` clause. ## Fixes - `JsonifiedValue` now matches `AsyncIteratorClass` directly (its own case, ahead of the generic `AsyncIteratorObject` branch), so the return type inference is no longer poisoned. - Jsonified iterator outputs are now typed as `AsyncIteratorClass`, matching what clients actually receive at runtime. ## Testing - New type tests cover `JsonifiedValue` over `AsyncIteratorClass` and the issue's end-to-end reproduction through `JsonifiedClient`; all three assertions fail with `TReturn = any` before the fix and pass after. - Root `tsc` and eslint are clean.
1 parent 324bd7a commit adde78c

2 files changed

Lines changed: 20 additions & 3 deletions

File tree

packages/openapi/src/types.test-d.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type { Client, ORPCError } from '@orpc/client'
2+
import type { RouterContractClient } from '@orpc/contract'
3+
import type { AsyncIteratorClass } from '@orpc/shared'
24
import type { JsonifiedClient, JsonifiedValue } from './types'
5+
import { asyncIteratorObject, oc } from '@orpc/contract'
6+
import z from 'zod'
37

48
describe('JsonifiedValue', () => {
59
it('flat', () => {
@@ -18,7 +22,9 @@ describe('JsonifiedValue', () => {
1822
expectTypeOf<JsonifiedValue<Set<number>>>().toEqualTypeOf<number[]>()
1923
expectTypeOf<JsonifiedValue<Array<number>>>().toEqualTypeOf<number[]>()
2024
expectTypeOf<JsonifiedValue<{ a: number, b: Date }>>().toEqualTypeOf<{ a: number, b: string }>()
21-
expectTypeOf<JsonifiedValue<AsyncGenerator<Date, Date>>>().toEqualTypeOf<AsyncIteratorObject<string, string>>()
25+
expectTypeOf<JsonifiedValue<AsyncIteratorClass<Date, Date>>>().toEqualTypeOf<AsyncIteratorClass<string, string>>()
26+
expectTypeOf<JsonifiedValue<AsyncGenerator<Date, Date>>>().toEqualTypeOf<AsyncGenerator<string, string>>()
27+
expectTypeOf<JsonifiedValue<AsyncIteratorObject<Date, Date>>>().toEqualTypeOf<AsyncIteratorObject<string, string>>()
2228

2329
expectTypeOf<JsonifiedValue<DateConstructor>>().toEqualTypeOf<unknown>()
2430
})
@@ -41,6 +47,14 @@ describe('JsonifiedClient', () => {
4147
>()
4248
})
4349

50+
it('preserves event iterator yield/return types', () => {
51+
const contract = oc.output(asyncIteratorObject(z.date(), z.date()))
52+
53+
expectTypeOf<
54+
Awaited<ReturnType<JsonifiedClient<RouterContractClient<typeof contract>>>>
55+
>().toEqualTypeOf<AsyncIteratorClass<string, string>>()
56+
})
57+
4458
it('nested', () => {
4559
expectTypeOf<JsonifiedClient<{
4660
ping: Client<{ cache?: boolean }, { now: Date }, { b: Set<Date> }, Error | ORPCError<string, { a: Date }>>

packages/openapi/src/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// eslint-disable-next-line no-restricted-imports
22
import type { OpenAPIV3_1 } from '@hey-api/spec-types'
33
import type { AnyNestedClient, Client, ORPCError } from '@orpc/client'
4+
import type { AsyncIteratorClass } from '@orpc/shared'
45

56
export type OpenAPIDocument = OpenAPIV3_1.Document
67
export type OpenAPIOperationObject = OpenAPIV3_1.OperationObject
@@ -21,8 +22,10 @@ export type JsonifiedValue<T>
2122
: T extends URL ? string
2223
: T extends Map<infer K, infer V> ? JsonifiedArray<[K, V][]>
2324
: T extends Set<infer U> ? JsonifiedArray<U[]>
24-
: T extends AsyncIteratorObject<infer U, infer V> ? AsyncIteratorObject<JsonifiedValue<U>, JsonifiedValue<V>>
25-
: unknown
25+
: T extends AsyncIteratorClass<infer U, infer V> ? AsyncIteratorClass<JsonifiedValue<U>, JsonifiedValue<V>>
26+
: T extends AsyncGenerator<infer U, infer V> ? AsyncGenerator<JsonifiedValue<U>, JsonifiedValue<V>>
27+
: T extends AsyncIteratorObject<infer U, infer V> ? AsyncIteratorObject<JsonifiedValue<U>, JsonifiedValue<V>>
28+
: unknown
2629

2730
export type JsonifiedArray<T extends Array<unknown>> = T extends readonly []
2831
? []

0 commit comments

Comments
 (0)