Svelte Query `QueriesOptions` 类型别名全解析:createQueries 的类型推断内核

发布时间:2026/9/10 19:47:31
Svelte Query `QueriesOptions` 类型别名全解析:createQueries 的类型推断内核 Svelte QueryQueriesOptions类型别名全解析createQueries 的类型推断内核【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/queryQueriesOptions是 tanstack/svelte-query 中createQueries组合查询 API 的类型级心脏它负责把传入的查询选项元组/数组在编译期递归展开逐项推断并约束每个查询的TQueryFnData、TError、TData与TQueryKey类型参数。本文将以该类型别名的源码实现为主线逐层拆解其递归算法、三个类型参数的职责并结合QueriesResults、createQueries函数签名与仓库内的类型测试说明如何用它写出既安全又灵活的并行查询代码。类型定义与定位QueriesOptions定义在 packages/svelte-query/src/createQueries.svelte.ts:129并被 packages/svelte-query/src/index.ts:11 作为类型导出。文档原文给出的完整签名如下type QueriesOptionsT, TResults, TDepth TDepth[length] extends MAXIMUM_DEPTH ? CreateQueryOptionsForCreateQueries[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueriesHead] : T extends [infer Head, ...(infer Tails)] ? QueriesOptions[...Tails], [...TResults, GetCreateQueryOptionsForCreateQueriesHead], [...TDepth, 1] : ReadonlyArrayunknown extends T ? T : T extends CreateQueryOptionsForCreateQueriesinfer TQueryFnData, infer TError, infer TData, infer TQueryKey[] ? CreateQueryOptionsForCreateQueriesTQueryFnData, TError, TData, TQueryKey[] : CreateQueryOptionsForCreateQueries[];它的官方注释只有一句话QueriesOptions reducer recursively unwraps function arguments to infer/enforce type paramQueriesOptions 归约器递归解包函数参数以推断/强制类型参数。这句话点明了它的双重使命推断infer当用户不显式传类型参数时从每个查询选项的queryFn、select、throwOnError等字段中自动推导出数据类型强制enforce当用户显式传入类型参数时对initialData、placeholderData、select入参等进行类型约束编译期拦截错误用法。三个类型参数的含义QueriesOptions接受三个类型参数其中后两个有默认值类型参数约束默认值职责Textends any[]无传入的查询选项元组/数组类型即queries数组的字面量类型TResultsextends any[][]归约过程中的累积结果每次处理一个Head就把对应的GetCreateQueryOptionsForCreateQueriesHead追加进去TDepthextends ReadonlyArraynumber[]递归深度计数器每处理一层追加一个1用于触发MAXIMUM_DEPTH兜底防止 TS 递归深度超限TResults与TDepth是典型的reducer 累加器accumulator设计QueriesOptions不直接返回最终类型而是通过不断自我调用把中间结果往后传递。这正是文档将其命名为 reducer 的原因。逐分支拆解递归算法QueriesOptions的本质是一个条件类型级联conditional type chain按优先级依次匹配以下分支1. 深度兜底TDepth[length] extends MAXIMUM_DEPTHTDepth[length] extends MAXIMUM_DEPTH ? CreateQueryOptionsForCreateQueries[] : ...当累积深度达到上限MAXIMUM_DEPTH时放弃逐项推断直接返回CreateQueryOptionsForCreateQueries[]数组类型。这个常量定义在同文件 packages/svelte-query/src/createQueries.svelte.ts:37// Avoid TS depth-limit error in case of large array literal type MAXIMUM_DEPTH 20源码注释明确说明这是为了避免大型数组字面量触发 TS 深度限制错误。这意味着当queries数组的元素超过 20 个时例如用Array.map批量生成类型系统会退化为普通数组放弃保留每个位置独立的类型信息——这是性能与精度的刻意权衡。2. 空元组T extends []T extends [] ? [] : ...空数组直接映射为空结果[]对应queries: []的场景此时返回值也是空元组。3. 单元素元组T extends [infer Head]T extends [infer Head] ? [...TResults, GetCreateQueryOptionsForCreateQueriesHead]只剩最后一个元素时把它的处理结果追加到累积结果上并终止递归。4. 多元素元组T extends [infer Head, ...(infer Tails)]递归主分支QueriesOptions [...Tails], [...TResults, GetCreateQueryOptionsForCreateQueriesHead], [...TDepth, 1] 取出Head处理后追加进TResults同时把Tails作为新的T、深度加一继续递归。这是整个算法的核心循环逐元素解构、逐个映射类型。5. 通用数组Array.map产物ReadonlyArrayunknown extends TReadonlyArrayunknown extends T ? T : ...当T不是元组而是普通数组如Array.map()返回的CreateQueryOptions[]由于无法区分每个元素的位置直接原样返回T交由后续分支处理。6. 同质数组类型推断T extends CreateQueryOptionsForCreateQueriesinfer TQueryFnData, infer TError, infer TData, infer TQueryKey[] ? CreateQueryOptionsForCreateQueriesTQueryFnData, TError, TData, TQueryKey[] : ...源码注释对此分支给出了精确说明If T issomearray but we couldnt assign unknown[] to it, then it must hold some known/homogeneous type! use this to infer the param types in the case of Array.map() argument即T是某种数组但无法赋给unknown[]说明它保存着已知的同构类型。此时从CreateQueryOptionsForCreateQueriesTQueryFnData, TError, TData, TQueryKey[]中解出四个类型参数再以同一组泛型参数重建数组。这保证了Array.map生成的大批量查询如 50 条虽然无法逐项区分仍能保持统一的TQueryFnData/TData类型同时规避深度限制。7. 兜底CreateQueryOptionsForCreateQueries[]所有分支都不匹配时退化为不带泛型参数的CreateQueryOptionsForCreateQueries[]类型安全降级但不报错。支撑类型CreateQueryOptionsForCreateQueries 与 GetCreateQueryOptionsForCreateQueriesCreateQueryOptionsForCreateQueries定义于 packages/svelte-query/src/createQueries.svelte.ts:24// This defines the CreateQueryOptions that are accepted in QueriesOptions GetOptions. // placeholderData function always gets undefined passed type CreateQueryOptionsForCreateQueries TQueryFnData unknown, TError DefaultError, TData TQueryFnData, TQueryKey extends QueryKey QueryKey, OmitKeyof CreateQueryOptionsTQueryFnData, TError, TData, TQueryKey, placeholderData { placeholderData?: TQueryFnData | QueriesPlaceholderDataFunctionTQueryFnData }它是createQueries接受的查询选项类型基于通用的CreateQueryOptions但用OmitKeyof剔除原placeholderData后重新定义——placeholderData 的函数形式始终接收undefined作为参数注释点明类型为TQueryFnData | QueriesPlaceholderDataFunctionTQueryFnData。四个泛型参数均有默认值TQueryFnData unknown、TError DefaultError、TData TQueryFnData、TQueryKey extends QueryKey QueryKey。GetCreateQueryOptionsForCreateQueries定义于 packages/svelte-query/src/createQueries.svelte.ts:42是QueriesOptions处理单个元素的核心工具内部又分三部分Part 1对象形式显式类型参数若元素形如{ queryFnData, error?, data }则从对象字段提取三个类型参数构造CreateQueryOptionsForCreateQueries只给queryFnData时补默认TError只给data/error时TQueryFnData置为unknown。Part 2元组形式显式类型参数若元素是[TQueryFnData, TError, TData]形式的元组同样提取并构造缺省项依次补默认值。Part 3无显式参数时的推断与强制从queryFn?: QueryFunctioninfer TQueryFnData, infer TQueryKey | SkipTokenForCreateQueries、select?: (data: any) infer TData、throwOnError?: ThrowOnErrorany, infer TError, any, any三个字段中分别推断类型并做归一化处理CreateQueryOptionsForCreateQueries TQueryFnData, unknown extends TError ? DefaultError : TError, unknown extends TData ? TQueryFnData : TData, TQueryKey 即推断出的TError若为unknown则归一化为DefaultErrorTData若为unknown则回退为TQueryFnData因为未提供select时结果数据就是原始数据。SkipTokenForCreateQueries定义为symbol源码注释说明这是为了在 skipToken 非 immutable 时仍能加宽符号类型以启用推断见 createQueries.svelte.ts:40。姊妹类型 QueriesResults把输入映射到输出QueriesOptions负责映射输入查询选项其姊妹类型QueriesResults定义于 createQueries.svelte.ts:171同样导出自 index.ts负责映射输出查询结果type QueriesResultsT, TResults, TDepth TDepth[length] extends MAXIMUM_DEPTH ? CreateQueryResult[] : T extends [] ? [] : T extends [infer Head] ? [...TResults, GetCreateQueryResultHead] : T extends [infer Head, ...infer Tails] ? QueriesResults[...Tails], [...TResults, GetCreateQueryResultHead], [...TDepth, 1] : { [K in keyof T]: GetCreateQueryResultT[K] }两者的递归骨架完全同构同样的深度兜底、空元组、单元素、多元素分支差异只在末端QueriesResults用GetCreateQueryResultHead把每个查询选项映射为对应的CreateQueryResultTData, TError对于非元组数组QueriesResults使用映射类型{ [K in keyof T]: GetCreateQueryResultT[K] }按索引逐个映射与QueriesOptions的重建同质数组策略互补。GetCreateQueryResultcreateQueries.svelte.ts:95内部同样分为对象显式类型参数 / 元组显式类型参数 / 推断三部分并通过GetDefinedOrUndefinedQueryResultcreateQueries.svelte.ts:79做精细化处理当initialData是已定义的确定类型时返回DefinedCreateQueryResultTData, TErrordata不再包含undefined否则返回普通的CreateQueryResult。该类型还会对initialData为函数形式的情况递归检查其返回值类型。这是 Svelte Query 能在已提供 initialData时自动收窄data类型的底层机制。与 createQueries 的集成QueriesOptions的消费方是createQueries函数createQueries.svelte.ts:260其签名如下export function createQueries T extends Arrayany, TCombinedResult QueriesResultsT, ( createQueriesOptions: Accessor{ queries: | readonly [...QueriesOptionsT] | readonly [ ...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueriesT[K] }, ] combine?: (result: QueriesResultsT) TCombinedResult }, queryClient?: AccessorQueryClient, ): TCombinedResult要点queries字段接受readonly [...QueriesOptionsT]元组展开逐项约束或按索引映射的readonly [...{ [K in keyof T]: GetCreateQueryOptionsForCreateQueriesT[K] }]数组场景。AccessorT定义于 packages/svelte-query/src/types.ts:22export type AccessorT () T即createQueries的第一个参数是惰性函数返回{ queries, combine? }而非普通对象。这使得查询选项可以随 Svelte 5 的响应式状态如$derived、$props实时变化每次取值都会重新计算。返回值默认是QueriesResultsT每个查询对应一个CreateQueryResult的元组/数组若提供了combine则返回TCombinedResultcombine的返回值类型。queryClient?同样是AccessorQueryClient用于指定自定义 QueryClient不传时使用最近上下文中的那个。运行时实现的关键路径createQueries.svelte.ts:274-324通过$derived将选项解析为client.defaultQueryOptions(opts)补齐默认配置并设置_optimisticResultsisRestoring时为isRestoring否则为optimistic确保订阅前结果已处于 fetching 状态再基于QueriesObserver来自tanstack/query-core建立观察者用$effect订阅更新、$effect.pre同步setQueries。combine会作为QueriesObserverOptions的combine传入实现结果聚合。实战从文档示例到类型约束验证基础用法动态查询数组createQueries.md 与源码 JSDoc 给出的典型场景是根据一组 id 并行抓取文章script langts import { createQueries } from tanstack/svelte-query let { ids }: { ids: Arraynumber } $props() const postQueries createQueries(() ({ queries: ids.map((id) ({ queryKey: [post, id], queryFn: () fetchPost(id), staleTime: Infinity, })), })) /script ul {#each postQueries as query, index (ids[index])} {#if query.isPending} liLoading.../li {:else if query.isError} liError: {query.error.message}/li {:else} li{query.data.title}/li {/if} {/each} /ul这里ids.map(...)生成的是普通数组QueriesOptions走同质数组分支推断出ArrayCreateQueryResultTPost, Error{#each}中每个query.data都保持类型。注意{#each}用(ids[index])作为 key保证列表顺序稳定。combine 聚合把多个查询合并为一个响应式值script langts import { createQueries } from tanstack/svelte-query let { ids }: { ids: Arraynumber } $props() const combined createQueries(() ({ queries: ids.map((id) ({ queryKey: [post, id], queryFn: () fetchPost(id), })), combine: (postQueries) ({ data: postQueries.map((query) query.data), isPending: postQueries.some((query) query.isPending), isError: postQueries.some((query) query.isError), }), })) /script {#if combined.isPending} Loading... {:else if combined.isError} Error loading posts {:else} ul {#each combined.data as post} li{post?.title}/li {/each} /ul {/if}提供combine后返回类型从结果元组变为combine的返回类型此处为{ data: ...; isPending: boolean; isError: boolean }组件模板只需消费这一个聚合对象。显式类型参数元组形式与对象形式类型测试文件 packages/svelte-query/tests/createQueries/createQueries.test-d.ts 完整覆盖了QueriesOptions的各个分支。元组形式的显式类型参数按[TQueryFnData, TError, TData]传入const result createQueries [[number], [string], [Arraystring, boolean]] ( () ({ queries: [ { queryKey: key1, queryFn: () 1 }, { queryKey: key2, queryFn: () string }, { queryKey: key3, queryFn: () [string[]] }, ], }), () queryClient, ) // result[0] 为 CreateQueryResultnumber, unknown // result[2] 为 CreateQueryResultArraystring, boolean对象形式按{ queryFnData, error?, data }传入且测试证实**dataTData优先于queryFnDataTQueryFnData**const result2 createQueries [{ queryFnData: string; data: string }, { queryFnData: string; data: number }] (() ({ queries: [ /* queryFn 返回 stringselect 分别转小写/parseInt */ ], }), () queryClient) // result2[0] 为 CreateQueryResultstring, unknown // result2[1] 为 CreateQueryResultnumber, unknown对象形式还允许只给data而让TQueryFnData保持unknownconst result3 createQueries[{ data: string }, { data: number }](...) // select 回调的入参推断为 unknown返回值类型用于结果类型类型约束编译期拦截错误测试中大量使用ts-expect-error验证QueriesOptions的强制能力createQueries( () ({ queries: [ { queryKey: key1, queryFn: () string, placeholderData: string, // ts-expect-error (initialData: string) initialData: 123, // 必须与 TData(string) 一致 }, { queryKey: key2, queryFn: () 123, // ts-expect-error (placeholderData: number) placeholderData: string, // 必须与 TQueryFnData(number) 一致 initialData: 123, }, ], }), () queryClient, )显式类型参数场景下约束同样生效createQueries[[string, unknown, string], [string, boolean, number]]( () ({ queries: [ /* select 返回 string / number */ ], }), () queryClient, ) // ts-expect-error (initialData: string) initialData: 123 // 显式指定 TDatastring 后initialData 传 number 报错非法字段名也会被拒绝// ts-expect-error (invalidField) queries: Array(10).map(() ({ someInvalidField: }))。强类型 queryFn 工厂与包装函数测试还演示了QueriesOptions在类型化 queryFn 工厂 自定义包装函数场景下的表现通过QueryFunctionnumber, QueryKeyA等工厂构造选项后select的返回类型如[number, string]会被精确映射进结果类型CreateQueryResult[number, string], Error将强类型选项数组传入包装的useWrappedQueries时queries.map()内的queryFn与ctx.queryKey依然保有完整类型信息最终返回ArrayCreateQueryResultnumber, Error。运行时行为与测试佐证类型层面之外packages/svelte-query/tests/createQueries/createQueries.svelte.test.ts 验证了createQueries的运行时语义可与类型设计互相印证状态逐步收敛两个 pending 查询逐个 resolve 时结果从[{data: undefined}, {data: undefined}]依次变为[{data: 1}, {data: undefined}]、[{data: 1}, {data: 2}]且$effect总共只触发 3 次渲染——数据更新渲染一次、isFetching过渡不额外渲染track results 用例断言 3 次。空数组动态追加queries初始为空数组时结果为[]向响应式ref中追加查询后结果自动出现对应条目。combine 的响应式追踪combine内访问的属性data、refetch会被精确追踪refetch后数据无变化则不产生多余重渲染。isRestoring 期不发起请求useIsRestoring()为真时_optimisticResults置为isRestoring多个查询含不同耗时在恢复期均保持pending/idle且queryFn零调用。使用建议与注意事项优先让类型自动推断只要queryFn/select有明确返回类型QueriesOptions的 Part 3 推断已足够精确无需手写类型参数显式参数适用于需要指定TError如错误类型非Error或select入参无法自动推断的场景。利用 combine 减少渲染与模板复杂度聚合后的单一对象比逐项判断更利于模板组织同时combine的属性访问追踪保证了最小重渲染。理解 20 层深度上限超长数组字面量会退化为普通数组类型这是避免 TS 深度溢出的刻意设计大批量同构查询如 50 条应优先用Array.map生成走同质数组分支保持类型。initialData 的收窄效果传入确定的initialData后结果类型自动变为DefinedCreateQueryResultdata不再含undefined模板中可放心直接使用。首个参数是 Accessor务必用() ({ queries, combine })的形式传入才能让查询选项随 Svelte 5 响应式状态实时更新。关联文档导航类型定义QueriesOptions QueriesResults函数说明createQueries运行时行为createQueries.svelte.test.ts 类型契约createQueries.test-d.tsSvelte Query 全局概览docs/framework/svelte/overview.md【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询