TypeScript
Vue Query 现已使用 TypeScript 编写,以确保该库及你的项目具备类型安全性!
需注意以下几点:
- TanStack Query 遵循 DefinitelyTyped 的支持窗口,支持过去两年内发布的 TypeScript 版本。目前这意味着支持 TypeScript 5.7 及更高版本。
- 本仓库中类型定义的变更被视为 非破坏性变更,通常以 补丁(patch) 语义化版本号发布(否则每次类型增强都将触发主版本升级!)。
- 强烈建议你将 vue-query 包版本锁定至特定补丁版本,并在升级时预期类型可能在任意版本间被修复或升级
- Vue Query 中与类型无关的公共 API 仍严格遵循语义化版本规范。
类型推断
Vue Query 中的类型通常能顺畅地向下流动,因此你通常无需手动提供类型注解。
1const { data } = useQuery({2 // ^? const data: Ref<number> | Ref<undefined>3 queryKey: ['test'],4 queryFn: () => Promise.resolve(5),5})1const { data } = useQuery({2 // ^? const data: Ref<string> | Ref<undefined>3 queryKey: ['test'],4 queryFn: () => Promise.resolve(5),5 select: (data) => data.toString(),6})若你的 queryFn 具有明确定义的返回类型,则此机制效果最佳。请注意,大多数数据拉取库默认返回 any,因此请确保将其提取为一个具有正确类型的函数:
1const fetchGroups = (): Promise<Group[]> =>2 axios.get('/groups').then((response) => response.data)3 4const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })5// ^? const data: Ref<Group[]> | Ref<undefined>类型缩小
Vue Query 对查询结果使用了 可区分联合类型(discriminated union type),该联合类型通过 status 字段及派生的状态布尔标志进行区分。这使你可以检查例如 success 状态,从而确保 data 已定义:
1const { data, isSuccess } = reactive(2 useQuery({3 queryKey: ['test'],4 queryFn: () => Promise.resolve(5),5 }),6)7 8if (isSuccess) {9 data10 // ^? const data: number11}正是 reactive() 包装器实现了这一行为:它将结果展平为普通值,因此 isSuccess 和 data 保留在同一可区分联合类型中。若直接解构 useQuery(),则会得到彼此独立的 ref,而 TypeScript 无法将一个 ref 上的类型缩小传递至另一个 ref —— 此时 if (isSuccess.value) 会使 data.value 保持为 Group[] | undefined。若不使用 reactive(),则需直接对值 ref 进行类型缩小:
1const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })2 3if (data.value !== undefined) {4 data.value5 // ^? const data: Group[]6}为 error 字段指定类型
error 字段的默认类型为 Error,因为这是大多数用户的预期。
1const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })2// ^? const error: Ref<unknown>3 4if (error.value instanceof Error) {5 error.value6 // ^? const error: Error7}若你想抛出一个自定义错误,或完全不是 Error 的错误,可显式指定 error 字段的类型:
但此做法的缺点是:useQuery 的所有其他泛型参数将不再支持类型推断。通常认为,抛出非 Error 类型的值并非良好实践;因此,若你拥有类似 AxiosError 的子类,可使用 类型缩小 使 error 字段更具体:
注册全局 Error 类型
TanStack Query v5 提供了一种方式,通过扩展 Register 接口来为整个库设置全局 Error 类型,而无需在每次调用处显式指定泛型。这可确保类型推断继续生效,同时使 error 字段采用你指定的类型。若你希望强制调用方必须执行显式类型缩小,请将 defaultError 设为 unknown:
1import '@tanstack/vue-query'2 3declare module '@tanstack/vue-query' {4 interface Register {5 // Use unknown so call sites must narrow explicitly.6 defaultError: unknown7 }8}9 10const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })11// ^? const error: unknown | null为查询键和修改键指定类型
注册查询键和修改键类型
与注册 全局错误类型 类似,你也可注册全局 QueryKey 和 MutationKey 类型。这使你能为键提供更契合应用层级结构的组织方式,并在整个库的各处接口上保持其类型安全。注意:所注册的类型必须继承 Array 类型,以确保你的键始终为数组。
1import '@tanstack/vue-query'2 3type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]4 5declare module '@tanstack/vue-query' {6 interface Register {7 queryKey: QueryKey8 mutationKey: QueryKey9 }10}使用 skipToken 实现类型安全的查询禁用
若你正在使用 TypeScript,可借助 skipToken 来禁用查询。当你需要基于某个条件禁用查询、同时又希望保持查询的类型安全性时,此功能尤为有用。更多细节请参阅 禁用查询 指南。