TypeScript

Solid Query 使用 TypeScript 编写,以确保库和你的项目是类型安全的!

需要记住的事项

  • 当前类型需要使用 TypeScript v4.7 或更高版本
  • 此仓库中类型的更改被认为是非破坏性的,通常作为 patch semver 更改发布(否则每个类型增强都将是一个主要版本!)。
  • 强烈建议你将 solid-query 包版本锁定到特定的 patch 版本,并在升级时预期类型可能会在任何版本之间被修复或升级
  • Solid Query 的非类型相关的公共 API 仍然严格遵循 semver。

类型推断

Solid Query 中的类型通常可以很好地流动,因此你无需自己提供类型注释

tsx
import { createQuery } from '@tanstack/solid-query'

const query = createQuery(() => ({
  queryKey: ['number'],
  queryFn: () => Promise.resolve(5),
}))

query.data
//    ^? (property) data: number | undefined
import { createQuery } from '@tanstack/solid-query'

const query = createQuery(() => ({
  queryKey: ['number'],
  queryFn: () => Promise.resolve(5),
}))

query.data
//    ^? (property) data: number | undefined

typescript playground

tsx
import { createQuery } from '@tanstack/solid-query'

const query = createQuery(() => ({
  queryKey: ['test'],
  queryFn: () => Promise.resolve(5),
  select: (data) => data.toString(),
}))

query.data
//    ^? (property) data: string | undefined
import { createQuery } from '@tanstack/solid-query'

const query = createQuery(() => ({
  queryKey: ['test'],
  queryFn: () => Promise.resolve(5),
  select: (data) => data.toString(),
}))

query.data
//    ^? (property) data: string | undefined

typescript playground

如果你的 queryFn 具有明确定义的返回类型,这将效果最佳。请记住,大多数数据获取库默认返回 any,因此请务必将其提取到正确类型的函数中

tsx
const fetchGroups = (): Promise<Group[]> =>
  axios.get('/groups').then((response) => response.data)

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.data
//    ^? (property) data: Group[] | undefined
const fetchGroups = (): Promise<Group[]> =>
  axios.get('/groups').then((response) => response.data)

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.data
//    ^? (property) data: Group[] | undefined

typescript playground

类型收窄

Solid Query 为 query 结果使用 discriminated union type,由 status 字段和派生的状态布尔标志区分。这将允许你检查例如 success 状态,以使 data 被定义

tsx
const query = createQuery(() => ({
  queryKey: ['number'],
  queryFn: () => Promise.resolve(5),
}))

if (query.isSuccess) {
  const data = query.data
  //     ^? const data: number
}
const query = createQuery(() => ({
  queryKey: ['number'],
  queryFn: () => Promise.resolve(5),
}))

if (query.isSuccess) {
  const data = query.data
  //     ^? const data: number
}

typescript playground

为 error 字段添加类型

error 的类型默认为 Error,因为这是大多数用户所期望的。

tsx
const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: Error | null
const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: Error | null

typescript playground

如果你想抛出自定义错误,或者根本不是 Error 的东西,你可以指定 error 字段的类型

tsx
const query = createQuery<Group[], string>(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: string | null
const query = createQuery<Group[], string>(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: string | null

但是,这样做有一个缺点,即 useQuery 的所有其他泛型的类型推断将不再起作用。通常不建议抛出不是 Error 的东西,因此如果你有像 AxiosError 这样的子类,你可以使用类型收窄来使 error 字段更具体

tsx
import axios from 'axios'

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: Error | null

if (axios.isAxiosError(query.error)) {
  query.error
  //    ^? (property) error: AxiosError
}
import axios from 'axios'

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: Error | null

if (axios.isAxiosError(query.error)) {
  query.error
  //    ^? (property) error: AxiosError
}

typescript playground

注册全局 Error

TanStack Query v5 允许通过修改 Register 接口来为所有内容设置全局 Error 类型,而无需在调用端指定泛型。这将确保推断仍然有效,但 error 字段将是指定的类型

tsx
import '@tanstack/solid-query'

declare module '@tanstack/solid-query' {
  interface Register {
    defaultError: AxiosError
  }
}

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: AxiosError | null
import '@tanstack/solid-query'

declare module '@tanstack/solid-query' {
  interface Register {
    defaultError: AxiosError
  }
}

const query = createQuery(() => ({
  queryKey: ['groups'],
  queryFn: fetchGroups,
}))

query.error
//    ^? (property) error: AxiosError | null

注册全局 Meta

与注册全局错误类型类似,你也可以注册全局 Meta 类型。这确保了 queriesmutations 上的可选 meta 字段保持一致且类型安全。请注意,注册的类型必须扩展 Record<string, unknown>,以便 meta 仍然是一个对象。

ts
import '@tanstack/solid-query'

interface MyMeta extends Record<string, unknown> {
  // Your meta type definition.
}

declare module '@tanstack/solid-query' {
  interface Register {
    queryMeta: MyMeta
    mutationMeta: MyMeta
  }
}
import '@tanstack/solid-query'

interface MyMeta extends Record<string, unknown> {
  // Your meta type definition.
}

declare module '@tanstack/solid-query' {
  interface Register {
    queryMeta: MyMeta
    mutationMeta: MyMeta
  }
}

为 Query Options 添加类型

如果你将 query options 内联到 createQuery 中,你将获得自动类型推断。但是,你可能希望将 query options 提取到单独的函数中,以便在 createQuery 和例如 prefetchQuery 之间共享它们。在这种情况下,你将失去类型推断。要恢复它,你可以使用 queryOptions 辅助函数

ts
import { queryOptions } from '@tanstack/solid-query'

function groupOptions() {
  return queryOptions({
    queryKey: ['groups'],
    queryFn: fetchGroups,
    staleTime: 5 * 1000,
  })
}

createQuery(groupOptions)
queryClient.prefetchQuery(groupOptions())
import { queryOptions } from '@tanstack/solid-query'

function groupOptions() {
  return queryOptions({
    queryKey: ['groups'],
    queryFn: fetchGroups,
    staleTime: 5 * 1000,
  })
}

createQuery(groupOptions)
queryClient.prefetchQuery(groupOptions())

此外,从 queryOptions 返回的 queryKey 知道与其关联的 queryFn,我们可以利用该类型信息使 queryClient.getQueryData 等函数也意识到这些类型

ts
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

如果没有 queryOptionsdata 的类型将是 unknown,除非我们向其传递泛型

ts
const data = queryClient.getQueryData<Group[]>(['groups'])
const data = queryClient.getQueryData<Group[]>(['groups'])

使用 skipToken 安全地禁用 query

如果你正在使用 TypeScript,你可以使用 skipToken 来禁用 query。当你想根据条件禁用 query,但仍然希望保持 query 的类型安全时,这非常有用。

禁用 Queries 指南中阅读更多相关信息。