feat(clients): add ExtResult support to TanStack Query hooks#2490
feat(clients): add ExtResult support to TanStack Query hooks#2490genu wants to merge 3 commits intozenstackhq:devfrom
Conversation
Add InferSchema, InferOptions, and InferExtResult type utilities to client-helpers. Update React, Vue, and Svelte adapters to accept ClientContract type parameter, enabling computed fields from plugins to be reflected in result types. Simplify useClientQueries API by inferring schema and options from the client type. Update sliced-client tests to use new API surface.
📝 WalkthroughWalkthroughAdds InferSchema/InferExtResult/InferOptions and ORM-related types to client-helpers; threads an ExtResult generic through TanStack Query clients (React, Svelte, Vue), re-exports SchemaDef and the new helpers, and adjusts tests to derive client types from instantiated ZenStackClient objects. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/clients/tanstack-query/src/vue.ts (1)
134-148:⚠️ Potential issue | 🟠 MajorPreserve client-specific
Optionsin the Vue mutation result type.Line 142 still builds
SimplifiedResultwithQueryOptions<Schema>instead of theOptionsgeneric. That meansmutateAsyncresolves the sliced/narrowed type, but the returned mutation object'sdatais widened back to the generic schema shape.Suggested fix
export type ModelMutationModelResult< Schema extends SchemaDef, Model extends GetModels<Schema>, TArgs, Array extends boolean = false, Options extends QueryOptions<Schema> = QueryOptions<Schema>, ExtResult extends ExtResultBase<Schema> = {}, > = Omit< - ModelMutationResult<SimplifiedResult<Schema, Model, TArgs, QueryOptions<Schema>, false, Array, ExtResult>, TArgs>, + ModelMutationResult<SimplifiedResult<Schema, Model, TArgs, Options, false, Array, ExtResult>, TArgs>, 'mutateAsync' > & {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/clients/tanstack-query/src/vue.ts` around lines 134 - 148, The ModelMutationModelResult type currently constructs the Omit<>'d ModelMutationResult using SimplifiedResult<..., QueryOptions<Schema>, ...>, which widens the returned data; replace that QueryOptions<Schema> with the generic Options so the client-specific Options generic is preserved (update the SimplifiedResult type parameter in the Omit<> instantiation to use Options and ensure any other occurrences in ModelMutationModelResult that reference QueryOptions<Schema> are switched to Options, keeping mutateAsync's existing signature aligned).
🧹 Nitpick comments (1)
packages/clients/tanstack-query/test/react-sliced-client.test-d.ts (1)
8-20: Consider an explicitExtResultd.ts case here.All of these
Clienttypes come fromnew ZenStackClient(...), so they only exercise theSchema+Optionsinference path; the fifthClientContractgeneric stays at its default{}. That means this suite validates the new one-generic API shape, but not the computed-field inference this PR is adding. A small fixture with a non-emptyExtResultclient type would lock that down, and it can be mirrored in the Vue/Svelte suites.Also applies to: 31-41, 50-64, 77-85
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/clients/tanstack-query/test/react-sliced-client.test-d.ts` around lines 8 - 20, The tests only exercise Schema+Options inference for new ZenStackClient and omit the computed-field/ExtResult path; add a parallel TS fixture that explicitly supplies a non-empty ExtResult (the fifth ClientContract generic) to ZenStackClient so the type-checking covers computed-field inference — e.g., create a const/alias using new ZenStackClient<Schema, Options, ?, ?, { myComputedField: string }> or otherwise instantiate the client with an explicit ExtResult type and assert the expected derived types; apply the same explicit-ExtResult case to the other occurrences noted and mirror the change in the Vue and Svelte test suites.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@packages/clients/tanstack-query/src/vue.ts`:
- Around line 134-148: The ModelMutationModelResult type currently constructs
the Omit<>'d ModelMutationResult using SimplifiedResult<...,
QueryOptions<Schema>, ...>, which widens the returned data; replace that
QueryOptions<Schema> with the generic Options so the client-specific Options
generic is preserved (update the SimplifiedResult type parameter in the Omit<>
instantiation to use Options and ensure any other occurrences in
ModelMutationModelResult that reference QueryOptions<Schema> are switched to
Options, keeping mutateAsync's existing signature aligned).
---
Nitpick comments:
In `@packages/clients/tanstack-query/test/react-sliced-client.test-d.ts`:
- Around line 8-20: The tests only exercise Schema+Options inference for new
ZenStackClient and omit the computed-field/ExtResult path; add a parallel TS
fixture that explicitly supplies a non-empty ExtResult (the fifth ClientContract
generic) to ZenStackClient so the type-checking covers computed-field inference
— e.g., create a const/alias using new ZenStackClient<Schema, Options, ?, ?, {
myComputedField: string }> or otherwise instantiate the client with an explicit
ExtResult type and assert the expected derived types; apply the same
explicit-ExtResult case to the other occurrences noted and mirror the change in
the Vue and Svelte test suites.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1752260e-22ad-4ce9-a63c-81e82e0b0d3e
📒 Files selected for processing (7)
packages/clients/client-helpers/src/types.tspackages/clients/tanstack-query/src/react.tspackages/clients/tanstack-query/src/svelte/index.svelte.tspackages/clients/tanstack-query/src/vue.tspackages/clients/tanstack-query/test/react-sliced-client.test-d.tspackages/clients/tanstack-query/test/svelte-sliced-client.test-d.tspackages/clients/tanstack-query/test/vue-sliced-client.test-d.ts
ymc9
left a comment
There was a problem hiding this comment.
Thanks for adding this @genu . It's a cleaner and better approach. I should have done it this way in the previous release. The only downside is you'll pull in the full typing of the ORM client to the client side (more type-level dependencies), which is probably fine.
Please check my few comments.
| Array extends boolean = false, | ||
| Options extends QueryOptions<Schema> = QueryOptions<Schema>, | ||
| > = Omit<ModelMutationResult<SimplifiedResult<Schema, Model, TArgs, Options, false, Array>, TArgs>, 'mutateAsync'> & { | ||
| ExtResult extends ExtResultBase<Schema> = {}, |
There was a problem hiding this comment.
Hooks are not subject to result extension, so maybe we remove this parameter and pass {} down?
There was a problem hiding this comment.
Done — removed the ExtResult type parameter from ClientHooks, ModelQueryHooks, ModelMutationModelResult, and useModelQueries. Now passing {} directly everywhere. Also cleaned up the unused ExtResultBase and InferExtResult imports.
There was a problem hiding this comment.
Hi @genu , my apologies that I didn't make it clear in the previous review comment here. I wanted to say that "custom procedure hooks" are not subject to result extension. Other regular hooks should carry such typing as you did previously ... sorry for the trouble
- Rename `Client` type parameter to `SchemaOrClient` in useClientQueries
- Remove `ExtResult` type parameter from hooks types (ClientHooks,
ModelQueryHooks, ModelMutationModelResult, useModelQueries) since
hooks are not subject to result extension; pass `{}` directly instead
- Clean up unused ExtResultBase and InferExtResult imports
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/clients/tanstack-query/src/vue.ts`:
- Around line 138-145: The type ModelMutationResult currently hardcodes
QueryOptions<Schema> for the base mutation result which loses the generic
Options narrowing; update the base type so it uses the generic Options parameter
instead of QueryOptions<Schema> (i.e., replace the QueryOptions<Schema>
occurrence inside the Omit<SimplifiedResult<...>> with Options) so the overall
type and the mutateAsync signature both consistently reference Options
(affecting ModelMutationResult, SimplifiedResult and ModelMutationOptions
usage).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 83deb214-8e61-4520-bcc0-8c5de02a3f63
📒 Files selected for processing (3)
packages/clients/tanstack-query/src/react.tspackages/clients/tanstack-query/src/svelte/index.svelte.tspackages/clients/tanstack-query/src/vue.ts
…delResult The base mutation result type was hardcoding QueryOptions<Schema> instead of using the Options generic parameter, breaking consistency with React and Svelte adapters.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/clients/tanstack-query/src/vue.ts`:
- Around line 20-21: The Vue adapter is not threading the plugin-computed
extension type (InferExtResult) through its types and exports, causing plugin
fields to be lost; import and re-export InferExtResult alongside
InferOptions/InferSchema, then add an ExtResult generic (bounded by
InferExtResult) into the Vue-specific types that currently hardcode "{}" —
specifically update SimplifiedResult, SimplifiedPlainResult, all Find*Args
generics, and the return type of useClientQueries to accept and propagate
ExtResult so plugin-computed fields flow into query/mutation result types.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 1313625a-59ba-4010-95b1-71958f5f1774
📒 Files selected for processing (1)
packages/clients/tanstack-query/src/vue.ts
Add type inference helpers to client-helpers enabling computed fields from plugins to be reflected in result types across React, Vue, and Svelte adapters.
Changes:
InferSchema,InferOptions, andInferExtResulttype utilities toclient-helpersuseClientQueriesto accept aClientContracttype parameter instead of requiring separate schema + options genericsThis enables better type inference when using plugins that extend query results with computed fields.
Summary by CodeRabbit
New Features
Tests