React Query 现在使用 TypeScript 编写,以确保库和您的项目都是类型安全的!
需要注意的事项:
React Query 中的类型通常可以很好地传递,因此您不必自己提供类型注释
const { data } = useQuery({
// ^? const data: number | undefined
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
})
const { data } = useQuery({
// ^? const data: number | undefined
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
})
const { data } = useQuery({
// ^? const data: string | undefined
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
select: (data) => data.toString(),
})
const { data } = useQuery({
// ^? const data: string | undefined
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
select: (data) => data.toString(),
})
如果您的 queryFn 具有明确定义的返回类型,则此方法效果最佳。请记住,大多数数据获取库默认返回 any,因此请确保将其提取到正确类型的函数中:
const fetchGroups = (): Promise<Group[]> =>
axios.get('/groups').then((response) => response.data)
const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const data: Group[] | undefined
const fetchGroups = (): Promise<Group[]> =>
axios.get('/groups').then((response) => response.data)
const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const data: Group[] | undefined
React Query 对查询结果使用可区分联合类型,由 status 字段和派生的状态布尔标志进行区分。这将允许您检查例如 success 状态以使 data 定义:
const { data, isSuccess } = useQuery({
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
})
if (isSuccess) {
data
// ^? const data: number
}
const { data, isSuccess } = useQuery({
queryKey: ['test'],
queryFn: () => Promise.resolve(5),
})
if (isSuccess) {
data
// ^? const data: number
}
错误字段的类型默认为 Error,因为这是大多数用户所期望的。
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error
如果您想抛出自定义错误,或者根本不是 Error 的东西,您可以指定错误字段的类型:
const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
// ^? const error: string | null
const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
// ^? const error: string | null
然而,这样做的缺点是 useQuery 的所有其他泛型的类型推断将不再起作用。通常不认为抛出非 Error 的东西是一个好习惯,因此如果您有一个像 AxiosError 这样的子类,您可以使用_类型收窄_来使错误字段更具体:
import axios from 'axios'
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error | null
if (axios.isAxiosError(error)) {
error
// ^? const error: AxiosError
}
import axios from 'axios'
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error | null
if (axios.isAxiosError(error)) {
error
// ^? const error: AxiosError
}
TanStack Query v5 提供了一种为所有内容设置全局错误类型的方法,而无需在调用端指定泛型,方法是修改 Register 接口。这将确保推断仍然有效,但错误字段将是指定的类型:
import '@tanstack/react-query'
declare module '@tanstack/react-query' {
interface Register {
defaultError: AxiosError
}
}
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: AxiosError | null
import '@tanstack/react-query'
declare module '@tanstack/react-query' {
interface Register {
defaultError: AxiosError
}
}
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: AxiosError | null
与注册全局错误类型类似,您也可以注册全局 Meta 类型。这可确保查询和变更上的可选 meta 字段保持一致且类型安全。请注意,注册的类型必须扩展 Record<string, unknown>,以便 meta 保持为对象。
import '@tanstack/react-query'
interface MyMeta extends Record<string, unknown> {
// 您的元类型定义。
}
declare module '@tanstack/react-query' {
interface Register {
queryMeta: MyMeta
mutationMeta: MyMeta
}
}
import '@tanstack/react-query'
interface MyMeta extends Record<string, unknown> {
// 您的元类型定义。
}
declare module '@tanstack/react-query' {
interface Register {
queryMeta: MyMeta
mutationMeta: MyMeta
}
}
同样,与注册全局错误类型类似,您也可以注册全局 QueryKey 和 MutationKey 类型。这允许您为您的键提供更符合应用程序层次结构的结构,并在库的所有表面区域对它们进行类型化。请注意,注册的类型必须扩展 Array 类型,以便您的键保持为数组。
import '@tanstack/react-query'
type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]
declare module '@tanstack/react-query' {
interface Register {
queryKey: QueryKey
mutationKey: QueryKey
}
}
import '@tanstack/react-query'
type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]
declare module '@tanstack/react-query' {
interface Register {
queryKey: QueryKey
mutationKey: QueryKey
}
}
如果将查询选项内联到 useQuery 中,您将获得自动类型推断。但是,您可能希望将查询选项提取到一个单独的函数中,以便在 useQuery 和例如 prefetchQuery 之间共享它们。在这种情况下,您将失去类型推断。要恢复它,可以使用 queryOptions 辅助函数:
import { queryOptions } from '@tanstack/react-query'
function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}
useQuery(groupOptions())
queryClient.prefetchQuery(groupOptions())
import { queryOptions } from '@tanstack/react-query'
function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}
useQuery(groupOptions())
queryClient.prefetchQuery(groupOptions())
此外,从 queryOptions 返回的 queryKey 知道与其关联的 queryFn,我们可以利用该类型信息使诸如 queryClient.getQueryData 之类的函数也知道这些类型:
function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}
const data = queryClient.getQueryData(groupOptions().queryKey)
// ^? const data: Group[] | undefined
function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}
const data = queryClient.getQueryData(groupOptions().queryKey)
// ^? const data: Group[] | undefined
如果没有 queryOptions,data 的类型将是 unknown,除非我们向其传递一个泛型:
const data = queryClient.getQueryData<Group[]>(['groups'])
const data = queryClient.getQueryData<Group[]>(['groups'])
有关类型推断的提示和技巧,请参阅社区资源中的 React Query 和 TypeScript。要了解如何获得最佳的类型安全性,您可以阅读类型安全的 React Query。
如果您正在使用 TypeScript,则可以使用 skipToken 来禁用查询。当您想根据条件禁用查询,但仍希望保持查询类型安全时,这很有用。 在禁用查询指南中阅读更多相关信息。