fix(openapi): preserve event iterator return type in JsonifiedClient - #1797
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
More templates
@orpc/ai-sdk
@orpc/arktype
@orpc/bun
@orpc/client
@orpc/cloudflare
@orpc/contract
@orpc/experimental-effect
@orpc/evlog
@orpc/hibernation
@orpc/json-schema
@orpc/nest
@orpc/next
@orpc/openapi
@orpc/opentelemetry
@orpc/pinia-colada
@orpc/pino
@orpc/publisher
@orpc/ratelimit
@orpc/server
@orpc/shared
@orpc/swr
@orpc/tanstack-query
@orpc/trpc
@orpc/valibot
@orpc/zod
commit: |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes
JsonifiedValueinpackages/openapi/src/types.tsnow matchesAsyncIteratorClassdirectly (ahead of the structuralAsyncIteratorObjectbranch) and emitsAsyncIteratorClass<...>for both iterator flavors, so an event iterator's return type is no longer lost.- Added type tests in
packages/openapi/src/types.test-d.tscoveringJsonifiedValueoverAsyncGenerator/AsyncIteratorObject/AsyncIteratorClassplus an end-to-endJsonifiedClient<RouterContractClient<...>>reproduction of the issue.
I verified the change rather than trusting the description: with the fix, root tsc -b is clean; reverting types.ts to the pre-fix logic makes the new assertions fail (the first AsyncIteratorClass branch is load-bearing — dropping it breaks the end-to-end JsonifiedClient assertion at line 55). The new AsyncIteratorClass input branch matters because the asyncIteratorObject(...) schema outputs a concrete AsyncIteratorClass<...> (with private members) that does not satisfy the structural AsyncIteratorObject branch on its own.
The PR body's diagnosis ("TReturn inferred as any from the return(value?: any) signature") is a plausible account of the mechanism; in my reproduction the pre-fix failures surfaced as toEqualTypeOf type-identity mismatches rather than a bare any, but the described symptom and the fix are consistent and correct.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — this run covers commit a98dbbc8 ("improve"), which refines the fix landed in bc8feaa5:
JsonifiedValuenow returns a flavor-preserving type per input:AsyncIteratorClass→AsyncIteratorClass,AsyncGenerator→AsyncGenerator,AsyncIteratorObject→AsyncIteratorObject, with the concreteAsyncIteratorClasscase matched first. This is a better version than the previous iteration, which collapsed all iterator outputs intoAsyncIteratorClass.- Updated
types.test-d.tsassertions accordingly and removed the oldAsyncGenerator … → AsyncIteratorObjectassertion.
Verified on the new head: root tsc -b is clean, and the tests are meaningful — deleting the first AsyncIteratorClass branch makes both the JsonifiedValue<AsyncIteratorClass<…>> and the end-to-end JsonifiedClient assertions (lines 25 and 55) fail, so the branch is load-bearing.
DeepSeek Flash (free via Pullfrog for OSS) | 𝕏
…1798) v1 port of #1797. `JsonifiedClient` was losing an event iterator's return type: a contract with `eventIterator(z.number(), z.number())` produced an iterator with `TReturn = any` instead of the jsonified return type. `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 #1796 ## Fixes - `JsonifiedValue` now matches `AsyncIteratorClass` directly (its own case, ahead of the generic `AsyncIteratorObject` branch), so the return type inference is no longer poisoned. - `AsyncGenerator` and plain `AsyncIteratorObject` values now keep their own shape instead of being widened, matching the updated #1797. ## Testing - New type tests cover `JsonifiedValue` over `AsyncIteratorClass`/`AsyncGenerator`/`AsyncIteratorObject` and the end-to-end reproduction through `JsonifiedClient` (using v1's `eventIterator` + `ContractRouterClient`); three assertions fail before the fix and pass after. - Root `tsc -b`, `tsc --noEmit`, and eslint are clean.

JsonifiedClientwas losing an event iterator's return type: a contract witheventIterator(z.number(), z.number())producedAsyncIteratorObject<number, any, unknown>instead of a properly typed iterator.AsyncIteratorClass'sreturn(value?: any)signature made TypeScript inferTReturnasanywhen matched againstAsyncIteratorObject<infer U, infer V>, since inference works off actual method signatures rather than theimplementsclause.Fixes
JsonifiedValuenow matchesAsyncIteratorClassdirectly (its own case, ahead of the genericAsyncIteratorObjectbranch), so the return type inference is no longer poisoned.AsyncIteratorClass, matching what clients actually receive at runtime.Testing
JsonifiedValueoverAsyncIteratorClassand the issue's end-to-end reproduction throughJsonifiedClient; all three assertions fail withTReturn = anybefore the fix and pass after.tscand eslint are clean.