news 2026/9/8 20:43:34

lit-query 中 UndefinedInitialDataOptions 类型解析:initialData 可选查询选项的类型设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
lit-query 中 UndefinedInitialDataOptions 类型解析:initialData 可选查询选项的类型设计

lit-query 中 UndefinedInitialDataOptions 类型解析:initialData 可选查询选项的类型设计

【免费下载链接】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

UndefinedInitialDataOptions@tanstack/lit-queryqueryOptions()第三个重载签名所使用的查询选项类型,用于描述"initialData可以被省略或为undefined"这一类查询配置。本文基于官方参考文档与 queryOptions.ts 的源码实现,完整梳理该类型的定义、类型参数、它在queryOptions()重载体系中的位置,以及它与DefinedInitialDataOptionsUnusedSkipTokenOptions的分工关系,帮助你在 Lit 项目中编写类型安全的查询选项并理解其底层的类型推导机制。

类型定义与定义位置

该类型的完整定义(对应参考文档 UndefinedInitialDataOptions.md)如下:

type UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> = QueryObserverOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey> & object

源码中位于 packages/lit-query/src/queryOptions.ts#L58-L74,其真实展开形式为:

/** * Query options where `initialData` can be omitted or undefined. */ export type UndefinedInitialDataOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, > = QueryObserverOptions< TQueryFnData, TError, TData, TQueryFnData, TQueryKey > & { initialData?: | undefined | InitialDataFunction<NonUndefinedGuard<TQueryFnData>> | NonUndefinedGuard<TQueryFnData> }

即:以QueryObserverOptions为基底,再通过交叉类型把initialData字段显式收窄为可选的三种取值——undefined、返回非undefined数据的惰性函数、或者直接的非undefined数据。文档页面中呈现的initialData?声明与之一致:

optional initialData: | InitialDataFunction<NonUndefinedGuard<TQueryFnData>> | NonUndefinedGuard<TQueryFnData>;

注意:类型参数中的TQueryData(第四个位置参数)在传给QueryObserverOptions时被固定为TQueryFnData,这是 TanStack Query 统一选项类型的标准约定——查询函数返回的数据类型即缓存中的数据类型。

四个类型参数及其默认值

类型参数默认值 / 约束含义
TQueryFnDataunknown查询函数(queryFn)返回的原始数据类型
TErrorDefaultError查询失败时错误对象类型,DefaultError在未声明Register['defaultError']时回退为Error(见 types.ts#L45-L49)
TDataTQueryFnData最终暴露给消费者的数据类型,通常是TQueryFnData经过select转换后的结果
TQueryKey约束extends QueryKey,默认QueryKey查询键类型,QueryKey在未声明Register['queryKey']时回退为ReadonlyArray<unknown>

这四个默认值与参考文档 UndefinedInitialDataOptions.md 中 "Type Parameters" 一节完全一致。

它属于哪个函数:queryOptions()的三重载体系

UndefinedInitialDataOptions并非孤立存在,它是queryOptions()三个重载之一(第三个)的参数与返回基础。在 queryOptions.ts#L130-L139 中:

export function queryOptions< TQueryFnData = unknown, TError = DefaultError, TData = TQueryFnData, TQueryKey extends QueryKey = QueryKey, >( options: UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>, ): UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey> & { queryKey: DataTag<TQueryKey, TQueryFnData, TError> }

整个文件定义了三个语义上互相区分的选项类型,分别对应三个重载(详见 queryOptions 函数参考文档):

类型源码位置语义
DefinedInitialDataOptionsqueryOptions.ts#L16-L29initialData必填,保证查询数据一定处于 defined 状态;此时queryFn变为可选
UnusedSkipTokenOptionsqueryOptions.ts#L34-L53提供queryFn排除SkipTokenqueryFn类型被Exclude<..., SkipToken \| undefined>收窄)
UndefinedInitialDataOptionsqueryOptions.ts#L58-L74initialData可省略或为undefined,是最通用的兜底分支

三者通过重载排列顺序(DefinedInitialDataOptionsUnusedSkipTokenOptionsUndefinedInitialDataOptions)让 TypeScript 按“信息量从强到弱”的方式匹配:优先尝试推导“数据必然存在”的最强类型;若推导不出来(比如你既没有initialData也不排除skipToken),最终落入UndefinedInitialDataOptions分支。

真正的函数实现是同一个恒等函数(queryOptions.ts#L141-L143):

export function queryOptions(options: unknown) { return options }

也就是说queryOptions()在运行期不改变任何值,它的价值完全在编译期:把queryKey品牌化(branding)为DataTag<TQueryKey, TQueryFnData, TError>,使数据与错误类型能沿着queryKey在整个 TanStack Query API(如queryClient.getQueryData()prefetchQuery()等)之间传播。DataTag的定义在 packages/query-core/src/types.ts#L71-L80,其本质是通过两个 Symbol 键(dataTagSymbol/dataTagErrorSymbol)在类型上附加不可见的标记。

关键构成类型拆解:initialData、NonUndefinedGuard 与 QueryObserverOptions

UndefinedInitialDataOptions的可读性取决于它引用的三个底层类型,它们都来自@tanstack/query-core

1.QueryObserverOptionsinitialData基线

QueryObserverOptions继承自QueryOptions并强制queryKey必填(types.ts#L315-L325)。而initialData字段本身声明在QueryOptions中(types.ts#L259):

initialData?: TData | InitialDataFunction<TData>

UndefinedInitialDataOptions通过交叉类型把该字段重写为以NonUndefinedGuard<TQueryFnData>为准的可选变体,从而允许“不传initialData”这一合法场景,同时禁止把undefined字面量伪装成初始数据。

2.NonUndefinedGuard:把 undefined 挡在 initialData 门外

// packages/query-core/src/types.ts#L12 export type NonUndefinedGuard<T> = T extends undefined ? never : T

它把undefined类型折叠为never。因此当TQueryFnData推导为string | undefined这类含undefined的联合时,initialData的合法取值会被自动收窄,避免你用undefined作为“初始数据”——这在语义上是矛盾的(“没有初始数据”与“初始数据就是 undefined”不是一回事)。

3.InitialDataFunction:惰性初始数据

// packages/query-core/src/types.ts#L173 export type InitialDataFunction<T> = () => T | undefined

支持传入函数而非直接值,适合需要运行时计算初始数据的场景(例如从 URL 参数、本地存储或全局状态中恢复)。

在 Lit 项目中的实战用法

queryOptions()返回带DataTag品牌化queryKey的选项对象,供 lit-query 的查询控制器(如createQueryController,类型CreateQueryOptions从 queryOptions.ts 与 createQueryController.js 导出)等 API 消费。类型三兄弟均通过 packages/lit-query/src/index.ts#L55-L60 从包入口公开导出:

export type { DefinedInitialDataOptions, UndefinedInitialDataOptions, UnusedSkipTokenOptions, } from './queryOptions.js' export { queryOptions } from './queryOptions.js'

源码注释中自带的示例(queryOptions.ts#L83-L92)演示了带initialData的用法:

import { queryOptions } from '@tanstack/lit-query' const todosOptions = queryOptions({ queryKey: ['todos'], queryFn: fetchTodos, initialData: [], })

而在不需要初始数据、或数据尚未就绪的场景中,initialData可以直接省略,此时命中的正是UndefinedInitialDataOptions重载:

import { queryOptions } from '@tanstack/lit-query' // 不传 initialData:命中第三个重载,数据在首次加载前为 pending const userOptions = queryOptions({ queryKey: ['user', id], queryFn: () => fetchUser(id), })

从源码结构看,重载的匹配顺序保证了:只要你在选项里提供了“必然存在的数据”(initialData或不含skipTokenqueryFn),推导出的选项类型就会携带更强的类型信息;只有在最宽松的场景下才回落到UndefinedInitialDataOptions,此时data等结果属性会在类型上体现“可能为undefined”的语义,迫使消费者处理加载态。

小结

  • UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>定义于 packages/lit-query/src/queryOptions.ts#L58-L74,语义为“initialData可省略或为undefined”的查询选项,是queryOptions()第三个重载的参数与返回基础。
  • 它与DefinedInitialDataOptionsinitialData必填、数据必然 defined)和UnusedSkipTokenOptionsqueryFn必须存在且非skipToken)共同构成queryOptions()的三重载推导体系,运行期均为恒等透传,价值全在编译期的DataTag品牌化queryKey
  • initialData的合法取值由NonUndefinedGuard(types.ts#L12)与InitialDataFunction(types.ts#L173)联合约束,确保初始数据“要么不存在、要么必然有值”。
  • 相关文档可继续延伸阅读:DefinedInitialDataOptions、UnusedSkipTokenOptions、queryOptions 函数;完整可运行的 Lit 示例项目可参考 examples/lit/basic。

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 20:43:01

FastAPI 条件化 OpenAPI:用环境变量按需启用与禁用接口文档

FastAPI 条件化 OpenAPI&#xff1a;用环境变量按需启用与禁用接口文档 【免费下载链接】fastapi FastAPI framework, high performance, easy to learn, fast to code, ready for production 项目地址: https://gitcode.com/GitHub_Trending/fa/fastapi 导读 在生产环…

作者头像 李华
网站建设 2026/9/8 20:41:32

从vibe coding到SDD:我用AI Agent开发npm中文排版包的经验

1. 为什么我不再"一句话甩给 AI"&#xff0c;改回先写规格再写代码vibe coding 刚火的那阵&#xff0c;我的节奏基本是&#xff1a;在聊天窗口里描述一个需求&#xff0c;AI 直接吐出一坨代码&#xff0c;我粘贴、运行、报错、继续让它改。做两三屏的小脚本还好&…

作者头像 李华
网站建设 2026/9/8 20:41:22

Life Level-up Guide: Running the 90-Day Action Plan as an Evidence-Backed System

Life Level-up Guide: Running the 90-Day Action Plan as an Evidence-Backed System 【免费下载链接】up An advanced guide which might benefit you a lot &#x1f389; . 韩先凯的人生进阶指南 人生进阶指南 离谱的人生 人生进阶 离谱的英语学习指南/英语学习教程/英语学…

作者头像 李华
网站建设 2026/9/8 20:40:08

德国签证资料宣誓翻译认证:从办理渠道到避坑要点,一篇讲透

办理德国签证、留学、换驾照或移民手续时&#xff0c;"德国签证资料宣誓翻译认证"是绕不开的核心环节。很多申请人因为用了普通翻译件&#xff0c;或把"翻译"和"认证"混为一谈&#xff0c;导致材料被德国外管局、大学或使领馆退回。一、先把概念…

作者头像 李华