useQuery data is undefined even with initialData/placeholderData
Hey, i have a custom hook that returns a
useQuery that fetches the options for a select input. I have tried with both initialData and placeholderData set as an empty array (which typewise is not incorrect, since I can have a select without options until they are added), but the data returned is set always set as <T | undefined> with T being the type of the options... Does it still require me to validate if isSuccess is true in order to set the type to T even though I have set the "default data"?
- @tanstack/react-query v4.24.4
- typescript v4.9.519 Replies
xenial-black•3y ago
can you show this in a sandbox please?
not sure if you're talking about a runtime or a type-level problem...
at runtime it should work in both cases
on type-level, only
initialData would narrow the typexenial-blackOP•3y ago
Sure, here it is: https://codesandbox.io/s/restless-mountain-h65p7z?file=/src/App.tsx
rfdomingues98
CodeSandbox
restless-mountain-h65p7z - CodeSandbox
restless-mountain-h65p7z by rfdomingues98 using @tanstack/react-query, @types/react, @types/react-dom, @types/uuid, @vitejs/plugin-react, autoprefixer, postcss, react, react-dom
xenial-blackOP•3y ago
If you hover over the
data variable, you see its type can be undefined even though i passed initialData to itxenial-black•3y ago
because you explicitly type as
UseQueryResult<TechnologiesType["items"], Error>, thus taking away type inference that makes this feature possible 🤷
delete that and it worksxenial-blackOP•3y ago
Wow... My manager told me to explicitly type everything... I told him it wasn't a good idea, and here's why... Thank you very much! And sorry for such dumb question 🤦♂️
xenial-black•3y ago
this video is for your manager then. especially the last minutes where every TypeScript expert ever tells you to infer return types 😅
https://www.youtube.com/watch?v=I6V2FkW1ozQ
Theo - t3․gg
YouTube
The Dangers Of Return Types in TypeScript
I'm sorry Prime ❤️
#typescript
THANK YOU TO ALL THE AWESOME GUESTS
- Malte https://twitter.com/cramforce
- Trash @trash_dev
- Ben Holmes https://twitter.com/BHolmesDev/
- Josh Goldberg https://twitter.com/JoshuaKGoldberg
- Dax Raad https://twitter.com/thdxr
- Maple https://twitter.com/heyImMapleLeaf
- Alex https://twitter.com/alexdotjs/
- Ta...
xenial-blackOP•3y ago
Already had sent him that video, he agreed with some parts, disagreed with some other parts, but didn't really discuss with me anything about it... oh well, might change his mind with this situation
rival-black•3y ago
Sorry to kind of highjack the thread but I have a similar problem
@TkDodo 🔮 is there a way to accept options as a function wrapper and keep the initialData nont undefined feature ?
I tried to replicate the option type but when I pass the
initialData to my abstraction via options, my data stays undefined
xenial-black•3y ago
only with overloads like we do internally
like-gold•3y ago
We faced a similar issue in our codebase and what solved it for us is "does your hook actually need to accept every possible option that useQuery can accept?". For example, we were trying to have our custom hook accept the whole
options object while, in practice, we were only ever passing onSuccess and onError. Refactoring the hook to directly accept onSuccess and onError instead of options allowed us to keep the type inference.xenial-black•3y ago
⬆️ this is the way. I always advise to not allow passing all options. like, would you really want to have different
cacheTimes passed from consumers?rival-black•3y ago
Yeah I'll do the same, indeed we don't use that many options too
thx both of you !
eastern-cyan•3y ago
@TkDodo 🔮 Hi, I kinda have the similar problem with
initialData. So here's the example
To give You some context, I want useAccount to accept initialData but only in some cases, so I make it optional. QueryFn always return the IAccount type so the problem hides inside the initialData itself, when I remove it, or make it required my guard works just fine so the account.data is always defined, but with initialData being optional, everything falls apart.
Do You have any idea how can I work around this problem?like-gold•3y ago
What if you only pass
initialData if it's actually present?
eastern-cyan•3y ago
This doesn't help. From what I can tell the problem is with types, cause when
initialData is present, useQuery actually doesn't go through loading state, it treats initialData as actual data in a fresh state. That's why we don't have hard loading state. So frankly speaking types don't lie, if I check account.isLoading it doesn't guard from data being undefined. The only work around I have found so far is to check if(account.data) instead of if(account.isLoading).
@TkDodo 🔮
I'm just curious if I get this problem right and if there's a better way to guard against this type of situations?!xenial-black•3y ago
Can you show a typescript playground reproduction please?
eastern-cyan•3y ago
nice-bash-vf5j7t
CodeSandbox is an online editor tailored for web applications.
xenial-black•3y ago
the
initialData you pass to useQuery is of type IAccount | undefined, hence we cannot narrow the type. If you want to replicate the behaviour that we do on your end, you either need conditional return types, or overloads (we use overloads)
or, you make two hooks: useAccount and useAcountWithInitialData, where initialData is not undefinedlike-gold•3y ago
I might be missing something but what I suggested above seems to remove the error?
https://codesandbox.io/p/sandbox/nice-bash-vf5j7t?file=%2FREADME.md
nice-bash-vf5j7t
CodeSandbox is an online editor tailored for web applications.