You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Tanstack Svelte Query中的TypeScript类型收窄问题?

解决Svelte + Tanstack Query中的TypeScript类型收窄问题

问题背景

将应用从React迁移到Svelte时,使用Tanstack Svelte Query遇到类型收窄失效:

  • 查询queryGetMyPageCollection()返回PageCollection | false
  • PageCollection的getPageByPath()方法返回Page | undefined
  • 模板中已在#if语句检查$query.data存在且getPageByPath()返回值非空,但TypeScript仍报错<DisplayPage>的page属性接收了undefined类型

错误信息:

Error: Type 'Page | undefined' is not assignable to type 'Page'.
Type 'undefined' is not assignable to type 'Page'. (ts)

原因分析

React的JSX中,TypeScript可以跟踪同一表达式的类型收窄,但Svelte模板的编译逻辑不同:两次调用$query.data.getPageByPath(data.slug)会被视为独立操作,TypeScript无法确认两次调用的返回值完全一致,因此无法自动收窄类型。

正确解决方式

方式一:模板内缓存结果(推荐)

使用Svelte的{#let}指令缓存getPageByPath()的结果,确保只调用一次,让TypeScript能跟踪到类型收窄:

<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>
        {#let foundPage = $query.data ? $query.data.getPageByPath(data.slug) : undefined}
            {#if foundPage}
                <p>Hooray! We found your page!</p>
                <DisplayPage page={foundPage} />
            {:else}
                <p>Oh 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}
        {/let}
    {/if}
</div>

方式二:在Script中派生可读存储

如果需要在多个地方复用该结果,可以创建一个派生的可读存储,提前处理类型收窄:

<script lang="ts">
  import { derived } from 'svelte/store';
  export let data;
  const query = createQuery({
        queryKey: queryKeyUsePages,
        queryFn: async () => queryGetMyPageCollection()
    });

  // 派生存储,自动处理类型收窄
  const foundPage = derived(query, ($query) => {
    if (!$query.data || $query.status !== 'success') return undefined;
    return $query.data.getPageByPath(data.slug);
  });
</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 $foundPage}
            <p>Hooray! We found your page!</p>
            <DisplayPage page={$foundPage} />
        {:else}
            <p>Oh 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>

说明

两种方式核心都是避免重复调用可能返回undefined的方法,通过缓存或提前计算的方式让TypeScript明确知道变量的类型,从而消除错误。

内容的提问来源于stack exchange,提问作者Patrick Kenny

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 20:37:07