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-query中queryOptions()第三个重载签名所使用的查询选项类型,用于描述"initialData可以被省略或为undefined"这一类查询配置。本文基于官方参考文档与 queryOptions.ts 的源码实现,完整梳理该类型的定义、类型参数、它在queryOptions()重载体系中的位置,以及它与DefinedInitialDataOptions、UnusedSkipTokenOptions的分工关系,帮助你在 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 统一选项类型的标准约定——查询函数返回的数据类型即缓存中的数据类型。
四个类型参数及其默认值
| 类型参数 | 默认值 / 约束 | 含义 |
|---|---|---|
TQueryFnData | unknown | 查询函数(queryFn)返回的原始数据类型 |
TError | DefaultError | 查询失败时错误对象类型,DefaultError在未声明Register['defaultError']时回退为Error(见 types.ts#L45-L49) |
TData | TQueryFnData | 最终暴露给消费者的数据类型,通常是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 函数参考文档):
| 类型 | 源码位置 | 语义 |
|---|---|---|
DefinedInitialDataOptions | queryOptions.ts#L16-L29 | initialData必填,保证查询数据一定处于 defined 状态;此时queryFn变为可选 |
UnusedSkipTokenOptions | queryOptions.ts#L34-L53 | 提供queryFn且排除SkipToken(queryFn类型被Exclude<..., SkipToken \| undefined>收窄) |
UndefinedInitialDataOptions | queryOptions.ts#L58-L74 | initialData可省略或为undefined,是最通用的兜底分支 |
三者通过重载排列顺序(DefinedInitialDataOptions→UnusedSkipTokenOptions→UndefinedInitialDataOptions)让 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.QueryObserverOptions与initialData基线
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或不含skipToken的queryFn),推导出的选项类型就会携带更强的类型信息;只有在最宽松的场景下才回落到UndefinedInitialDataOptions,此时data等结果属性会在类型上体现“可能为undefined”的语义,迫使消费者处理加载态。
小结
UndefinedInitialDataOptions<TQueryFnData, TError, TData, TQueryKey>定义于 packages/lit-query/src/queryOptions.ts#L58-L74,语义为“initialData可省略或为undefined”的查询选项,是queryOptions()第三个重载的参数与返回基础。- 它与
DefinedInitialDataOptions(initialData必填、数据必然 defined)和UnusedSkipTokenOptions(queryFn必须存在且非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),仅供参考