Can `useQuery`'s result type figure out that all errors throw?
It's wonderful that the typings can reassure me that if status is neither pending nor error, I can rely on data being defined. It seems that it would be even better if they could somehow realize that because
throwOnError
is set, one only needs to eliminate pending
in order to guarantee this state of affairs. Is this a deliberate design decision, am I doing things wrongly, or is it some sort of inherent TypeScript limitation?7 Replies
correct-apricot•3mo ago
By default typescript won't tell you if a function will throw or not
If you use something like neverthrow or effect.ts , you can get result types that'll have error types
adverse-sapphireOP•3mo ago
Thank you for your response. At a brief glance, both of those libraries you mentioned seem to be more than I was looking to add to the mix of things. I was hoping that some sort of conditional type magic could discern that if the options given contain a truthy value for throwOnError, the type of
useQuery
's return could be narrowed to exclude the error states, as it is known that they cannot actually come back to the caller.solid-orange•3mo ago
The return type of your function is all that is used to determine the type of data. As far as I am aware, if you do not manually provide the generics, the only way that Error is inferred is by the type you define in your
throwOnError
or retry
I believe.
For example, your options are
If you use queryOptions
to create your queries, you can define the types on that, and then have useQuery
infer based on the queryOptions as well.adverse-sapphireOP•3mo ago
Perhaps some sample code will illustrate my concern better.
If I explicitly eliminate both the pending and error possibilities,
TypeScript is able to narrow the result sufficiently for the following
dereference all the way to the inner
foo
without concern:
However, if I only check for pending, the final line causes complaints that
tqrv.data
might be undefined
:
Because qo
's throwOnError
is statically known to be true
, I thought there was no possible
way for the calling code to ever get an error result, and the above code should
work just like the first iteration.
The best I have managed to do so far is to wrap useQuery
like this:
But even doing it this way, I have to explicitly throw an error that should provably
never happen.solid-orange•3mo ago
Ah I see, sorry for the misunderstanding.
My understanding with queries is that you essentially have these return states available to you
- DefinedQueryObserverResult<TData, TError>
- QueryObserverLoadingErrorResult<TData, TError>
- QueryObserverLoadingResult<TData, TError>
- QueryObserverPendingResult<TData, TError>
- QueryObserverPlaceholderResult<TData, TError>;
None of these seem to, at a type level, infer whether or not the query can actually return any error data, so I suspect that currently it's just setup in a way that can infer this information for you in typescript.
I'm not a maintainer, so can't comment on if this is planned in the future, but my understanding is that you would either need to add a generic for whether it can throw or not and pass this all the way down; or set
TError
to some custom value such as a symbol, that they can check against in the types to use a new return type that defines data/loading/pending to be possible states, while removing errors.
What you essentially have here is half of a suspenseQuery 😂
Anyway, hopefully a maintainer could comment on whether I am wrong, or if this is something that is desired at all moving forward and if there is a timeline on making that type possibleadverse-sapphireOP•3mo ago
my understanding is that you would either need to add a generic for whether it can throw or notThis doesn't actually work (for one thing, it hits a snag on refresh errors), but it's illustrative of where I was aiming: I was hoping it could be done without resorting to template parameters that have to be set manually.
solid-orange•3mo ago
If nothing else, your naming is absolutely hilariously accurate.
useInfallibleQuery
and useSentientQuery
are great names, bravo