Commit 6685a85
Improve error when returning undefined in useLiveSuspenseQuery (#860)
* Fix: Allow useLiveSuspenseQuery to accept undefined to disable query
- Add overloads to support query functions that can return undefined/null
- Update implementation to return undefined values instead of throwing error when query is disabled
- Add tests for conditional query pattern with undefined
- Mirrors useLiveQuery behavior for consistency
This allows conditional queries like:
```ts
useLiveSuspenseQuery(
(q) => userId
? q.from({ users }).where(({ users }) => eq(users.id, userId)).findOne()
: undefined,
[userId]
)
```
* Add changeset for useLiveSuspenseQuery undefined support
* Revert: useLiveSuspenseQuery should not support disabled queries
Following TanStack Query's useSuspenseQuery design philosophy, disabled
queries are intentionally not supported to maintain the type guarantee
that data is T (not T | undefined).
Changes:
- Revert type overloads that allowed undefined/null returns
- Keep error throw when query callback returns undefined/null
- Improve error message with clear guidance on alternatives:
1. Use conditional rendering (don't render component until ready)
2. Use useLiveQuery instead (supports isEnabled flag)
- Update tests to expect error instead of undefined values
- Update changeset to document the design decision and alternatives
This matches TanStack Query's approach where Suspense queries prioritize
type safety and proper component composition over flexibility.
* Add comprehensive JSDoc documentation for useLiveSuspenseQuery
Add detailed @remarks section that appears in IDE tooltips to clearly
explain that disabled queries are not supported and provide two
alternative solutions:
1. Use conditional rendering (recommended pattern)
2. Use useLiveQuery instead (supports isEnabled flag)
Includes clear examples showing both ❌ incorrect and ✅ correct patterns.
This provides immediate guidance when users encounter the type error,
without requiring complex type-level error messages that can be fragile.
TypeScript's natural "No overload matches this call" error combined
with the JSDoc tooltip provides a good developer experience.
* WIP: Experiment with unconstructable types for custom type errors
Add 'poison pill' overloads that return DisabledQueryError type with
custom error message embedded via unique symbol. This is experimental
to evaluate if it provides better DX than JSDoc alone.
Still evaluating trade-offs before finalizing approach.
* Success: Unconstructable types work with proper implementation signature
The key insight: make the implementation signature return type a union
that includes both DisabledQueryError and the valid return types.
TypeScript requires overload signatures to be compatible with the
implementation signature. By using a union type:
DisabledQueryError | { state: any; data: any; collection: any }
We satisfy TypeScript's compatibility requirement while still catching
invalid patterns at compile time.
What users experience:
1. Type error when returning undefined → DisabledQueryError inferred
2. Property access errors: "Property 'data' does not exist on type 'DisabledQueryError'"
3. IDE tooltip shows custom error message embedded in the type
4. Compilation fails (forces fix)
This provides BOTH:
- JSDoc documentation (in tooltips)
- Active type-level errors with custom messaging
* Fix: Reorder overloads to fix type inference
The poison pill overloads were matching BEFORE the specific overloads,
causing TypeScript to infer DisabledQueryError for valid queries.
TypeScript checks overloads top-to-bottom and uses the first match.
Since QueryBuilder is assignable to QueryBuilder | undefined | null,
the poison pill overloads were matching first.
Solution: Move poison pill overloads to the END, just before the
implementation. This ensures:
1. Specific overloads (without undefined) match first
2. Poison pill overloads (with undefined) only match when needed
All tests now pass with no type errors.
* Revert poison pill overloads - keep improved error message only
The maintainer feedback showed that the poison pill overload approach
actually makes DX worse by removing the type error entirely. Users
don't get red squiggles at the call site - they only see the helpful
message when hovering, which is easy to miss.
**Before this revert (poison pill approach):**
- No type error at call site
- Return type changes to DisabledQueryError
- Users only discover issue when using result.data or if they hover
- Worse DX than the original "No overload matches" error
**After this revert:**
- TypeScript shows "No overload matches this call" error immediately
- Red squiggles prevent proceeding without fixing
- Clear runtime error with guidance if types are bypassed
- Comprehensive JSDoc documentation in IDE tooltips
This PR now delivers:
1. Improved runtime error message (clear, actionable guidance)
2. Enhanced JSDoc documentation (@remarks with examples)
3. Type errors that actually block compilation (not just change types)
Following TanStack Query's philosophy: simple, effective error handling
without clever type tricks that reduce clarity.
* ci: apply automated fixes
---------
Co-authored-by: Claude <[email protected]>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>1 parent 6503c09 commit 6685a85
File tree
2 files changed
+96
-2
lines changed- .changeset
- packages/react-db/src
2 files changed
+96
-2
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
74 | 107 | | |
75 | 108 | | |
76 | 109 | | |
| |||
146 | 179 | | |
147 | 180 | | |
148 | 181 | | |
149 | | - | |
| 182 | + | |
150 | 183 | | |
151 | | - | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
152 | 189 | | |
153 | 190 | | |
154 | 191 | | |
| |||
0 commit comments