TypeScript
Preact Query 现已使用 TypeScript 编写,以确保该库及你的项目具备类型安全性!
需注意以下几点:
- TanStack Query 遵循 DefinitelyTyped 的支持窗口,支持过去两年内发布的 TypeScript 版本。目前这意味着 TypeScript 5.7 及更高版本。
- 本仓库中类型定义的变更被视为 非破坏性变更,通常以 补丁(patch) 语义化版本形式发布(否则每次类型增强都将触发主版本升级!)。
- 强烈建议你将 preact-query 包版本锁定至特定补丁版本,并在升级时预期类型可能在任意版本间被修复或升级
- Preact Query 中与类型无关的公共 API 仍严格遵循语义化版本规范。
类型推断
Preact Query 中的类型通常能顺畅流动,因此你通常无需手动提供类型注解。
1const { data } = useQuery({2 // ^? const data: number | undefined3 queryKey: ['test'],4 queryFn: () => Promise.resolve(5),5})1const { data } = useQuery({2 // ^? const data: string | undefined3 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: Group[] | undefined类型缩小
Preact Query 对查询结果采用 可区分联合类型(discriminated union type),依据 status 字段及派生的状态布尔标志进行区分。这使你能通过检查例如 success 状态,来确保 data 已定义:
1const { data, isSuccess } = useQuery({2 queryKey: ['test'],3 queryFn: () => Promise.resolve(5),4})5 6if (isSuccess) {7 data8 // ^? const data: number9}为 error 字段指定类型
error 字段的默认类型为 Error,因为这是大多数用户的预期。
1const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })2// ^? const error: Error若你想抛出自定义错误,或完全不是 Error 的值,可显式指定 error 字段的类型:
1const { error } = useQuery<Group[], string>(['groups'], fetchGroups)2// ^? const error: string | null但此举的缺点是:useQuery 的其他所有泛型的类型推断将不再生效。通常不推荐抛出非 Error 类型的值;因此,若你拥有类似 AxiosError 的子类,可使用 类型缩小 使 error 字段更具体:
1import axios from 'axios'2 3const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })4// ^? const error: Error | null5 6if (axios.isAxiosError(error)) {7 error8 // ^? const error: AxiosError9}注册全局 Error 类型
TanStack Query v5 提供了一种方式,通过扩展 Register 接口,为所有位置设置全局 Error 类型,而无需在调用处显式指定泛型。这能确保类型推断继续生效,同时使 error 字段为指定类型。若你希望强制调用处必须执行显式类型缩小,请将 defaultError 设为 unknown:
1import '@tanstack/preact-query'2 3declare module '@tanstack/preact-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为 meta 字段指定类型
注册全局 Meta 类型
与注册 全局错误类型 类似,你也可注册一个全局 Meta 类型。这能确保 查询 和 修改 上的可选 meta 字段保持一致且具备类型安全性。注意:所注册的类型必须继承 Record<string, unknown>,以确保 meta 仍为对象。
1import '@tanstack/preact-query'2 3interface MyMeta extends Record<string, unknown> {4 // Your meta type definition.5}6 7declare module '@tanstack/preact-query' {8 interface Register {9 queryMeta: MyMeta10 mutationMeta: MyMeta11 }12}为查询键和修改键指定类型
注册查询键和修改键类型
同样地,除注册 全局错误类型 外,你也可注册全局 QueryKey 和 MutationKey 类型。这使你能为键提供更契合应用层级结构的组织方式,并在整个库的各处接口中保持其类型安全。注意:所注册的类型必须继承 Array 类型,以确保你的键仍为数组。
1import '@tanstack/preact-query'2 3type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]4 5declare module '@tanstack/preact-query' {6 interface Register {7 queryKey: QueryKey8 mutationKey: QueryKey9 }10}为查询选项指定类型
若你直接在 useQuery 中内联查询选项,将自动获得类型推断。但你可能希望将查询选项提取为独立函数,以便在 useQuery 和例如 query 之间共享。此时,你将丢失类型推断。为恢复推断,可使用 queryOptions 辅助函数:
1import { queryOptions } from '@tanstack/preact-query'2 3function groupOptions() {4 return queryOptions({5 queryKey: ['groups'],6 queryFn: fetchGroups,7 staleTime: 5 * 1000,8 })9}10 11useQuery(groupOptions())12queryClient.query(groupOptions())此外,queryOptions 返回的 queryKey 会知晓与其关联的 queryFn 类型,我们可利用该类型信息,使 queryClient.getQueryData 等函数也感知这些类型:
1function groupOptions() {2 return queryOptions({3 queryKey: ['groups'],4 queryFn: fetchGroups,5 staleTime: 5 * 1000,6 })7}8 9const data = queryClient.getQueryData(groupOptions().queryKey)10// ^? const data: Group[] | undefined若不使用 queryOptions,则 data 的类型将为 unknown,除非你为其传入泛型:
1const data = queryClient.getQueryData<Group[]>(['groups'])注意:通过 queryOptions 实现的类型推断 不适用于 queryClient.getQueriesData,因为它返回的是包含异构、unknown 数据的元组数组。若你确知查询将返回的数据类型,请显式指定:
1const entries = queryClient.getQueriesData<Group[]>(groupOptions().queryKey)2// ^? const entries: Array<[QueryKey, Group[] | undefined]>为修改选项指定类型
与 queryOptions 类似,你可使用 mutationOptions 将修改选项提取为独立函数:
1function groupMutationOptions() {2 return mutationOptions({3 mutationKey: ['addGroup'],4 mutationFn: addGroup,5 })6}7 8useMutation({9 ...groupMutationOptions(),10 onSuccess: () => queryClient.invalidateQueries({ queryKey: ['groups'] }),11})12useIsMutating(groupMutationOptions())13queryClient.isMutating(groupMutationOptions())使用 skipToken 实现类型安全的查询禁用
若你正在使用 TypeScript,可借助 skipToken 禁用查询。当你需要基于条件禁用查询但仍希望保持查询类型安全时,此功能尤为有用。更多详情请参阅 禁用查询 指南。
延伸阅读
有关类型推断的技巧与窍门,请参阅文章 Preact Query 和 TypeScript。若想获得尽可能高的类型安全性,可阅读 类型安全的 Preact Query。查询选项 API 阐述了 queryOptions 辅助函数如何协同类型推断工作。