Problem
I’m having trouble narrowing types with Tanstack Svelte Query. I am migrating my app from React to Svelte.
My query queryGetMyPageCollection() returns either an object PageCollection or false. PageCollection has a getPageByPath() method that returns a Page object or undefined.
This is where the problem occurs:
{#if $query.data && $query.data.getPageByPath(data.slug)}
<p>Hooray! We found your page!</p>
<DisplayPage page={$query.data.getPageByPath(data.slug)} />
I am checking $query.data, which is false or the PageCollection object, and, if the object exists, calling getPageByPath() against the query string to see if we can load a Page object. If the page object exists, then I display the page in the <DisplayPage> component.
This code works but TypeScript complains about the <DisplayPage> line:
Error: Type ‘Page | undefined’ is not assignable to type ‘Page’.
Type ‘undefined’ is not assignable to type ‘Page’. (ts)
However, I know that the getPageByPath() call for the <DisplayPage> component will not be undefined because I checked it in the #if statement.
Similar code works in React, so I’m guessing that in Svelte, TypeScript is not setting the type for the method call in the <DisplayPage> component based on the #if statement directly above it.
Question
How do I code this so that TypeScript does not produce an error? Please do not answer "set @tsignore"; I want to know how to actually solve this the TypeScript/Svelte way.
Full component code
<script lang="ts">
export let data;
const query = createQuery({
queryKey: queryKeyUsePages,
queryFn: async () => queryGetMyPageCollection()
});
</script>
<div>
{#if $query.isLoading}
<p>Loading...</p>
{:else if $query.isError}
<p>Error: {$query.error.message}</p>
{:else if $query.isSuccess}
<h1>Here is the page</h1>
{#if $query.data && $query.data.getPageByPath(data.slug)}
<p>Hooray! We found your page!</p>
<DisplayPage page={$query.data.getPageByPath(data.slug)} />
{:else}
<p>On no! We didn't find your page.</p>
{#if $query.data}
<p>Available paths:</p>
<ul>
{#each $query.data.pages as page}
<li><a href="/{page.path}">{page.title}</a></li>
{/each}
</ul>
{/if}
{/if}
{/if}
</div>
>Solution :
That should not work in React either, you need to store the result of the function call, otherwise the type will not be narrowed.
E.g. using @const:
{:else if $query.isSuccess}
<h1>Here is the page</h1>
{@const page = $query.data && $query.data.getPageByPath(data.slug)}
{#if page}
<p>Hooray! We found your page!</p>
<DisplayPage {page} />
{:else}
...