Framework
Version

TypeScript

React Query ist jetzt in TypeScript geschrieben, um sicherzustellen, dass die Bibliothek und Ihre Projekte typsicher sind!

Zu beachtende Punkte

  • Typen erfordern derzeit die Verwendung von TypeScript v4.7 oder höher
  • Änderungen an Typen in diesem Repository gelten als nicht-brechend und werden normalerweise als Patch-Semver-Änderungen veröffentlicht (andernfalls wäre jede Typverbesserung eine Hauptversion!).
  • Es wird dringend empfohlen, Ihre react-query-Paketversion auf eine bestimmte Patch-Version zu sperren und ein Upgrade zu erwarten, da Typen zwischen allen Releases behoben oder aktualisiert werden können
  • Die nicht typbezogene öffentliche API von React Query folgt weiterhin sehr streng semver.

Typinferenzen

Typen in React Query fließen im Allgemeinen sehr gut durch, sodass Sie keine Typannotationen selbst bereitstellen müssen.

tsx
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),
})

typescript playground

tsx
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(),
})

typescript playground

Dies funktioniert am besten, wenn Ihre queryFn einen klar definierten Rückgabetyp hat. Beachten Sie, dass die meisten Datenabrufbibliotheken standardmäßig any zurückgeben, stellen Sie also sicher, dass Sie sie in eine ordnungsgemäß typisierte Funktion extrahieren

tsx
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

typescript playground

Typ-Verengung

React Query verwendet einen diskriminierten Union-Typ für das Abfrageergebnis, der durch das Feld status und die abgeleiteten booleschen Statusflags diskriminiert wird. Dies ermöglicht es Ihnen, z. B. den success-Status zu überprüfen, um data zu definieren

tsx
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
}

typescript playground

Typisierung des Fehlerfelds

Der Typ für Fehler ist standardmäßig Error, da dies das ist, was die meisten Benutzer erwarten.

tsx
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: Error
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: Error

typescript playground

Wenn Sie einen benutzerdefinierten Fehler oder etwas werfen möchten, das kein Error ist, können Sie den Typ des Fehlerfelds angeben

tsx
const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
//      ^? const error: string | null
const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
//      ^? const error: string | null

Dies hat jedoch den Nachteil, dass die Typinferenz für alle anderen Generika von useQuery nicht mehr funktioniert. Es gilt im Allgemeinen nicht als gute Praxis, etwas anderes als einen Error auszulösen. Wenn Sie also eine Unterklasse wie AxiosError haben, können Sie Typ-Narrowing verwenden, um das Fehlerfeld spezifischer zu machen.

tsx
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
}

typescript playground

Registrierung eines globalen Fehlers

TanStack Query v5 ermöglicht es, einen globalen Fehlertyp für alles festzulegen, ohne Generika auf der Aufruferseite angeben zu müssen, indem das Register-Interface ergänzt wird. Dies stellt sicher, dass die Inferenz weiterhin funktioniert, aber das Fehlerfeld den angegebenen Typ hat. Wenn Sie erzwingen möchten, dass Aufrufer explizite Typ-Verengung durchführen müssen, setzen Sie defaultError auf unknown

tsx
import '@tanstack/react-query'

declare module '@tanstack/react-query' {
  interface Register {
    // Use unknown so call sites must narrow explicitly.
    defaultError: unknown
  }
}

const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: unknown | null
import '@tanstack/react-query'

declare module '@tanstack/react-query' {
  interface Register {
    // Use unknown so call sites must narrow explicitly.
    defaultError: unknown
  }
}

const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
//      ^? const error: unknown | null

Typing meta

Registrierung von globalem Meta

Ähnlich wie bei der Registrierung eines globalen Fehlertyps können Sie auch einen globalen Meta-Typ registrieren. Dies stellt sicher, dass das optionale Feld meta für Queries und Mutations konsistent und typsicher bleibt. Beachten Sie, dass der registrierte Typ Record<string, unknown> erweitern muss, damit meta ein Objekt bleibt.

ts
import '@tanstack/react-query'

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

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

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

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

Typisierung von Query- und Mutationsschlüsseln

Registrierung der Typen für Query- und Mutationsschlüssel

Ebenso wie bei der Registrierung eines globalen Fehlertyps können Sie auch einen globalen QueryKey- und MutationKey-Typ registrieren. Dies ermöglicht es Ihnen, Ihren Schlüsseln mehr Struktur zu geben, die der Hierarchie Ihrer Anwendung entspricht, und sie über die gesamte Oberfläche der Bibliothek typsicher zu machen. Beachten Sie, dass der registrierte Typ von Array erben muss, damit Ihre Schlüssel ein Array bleiben.

ts
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
  }
}

Typisierung von Query-Optionen

Wenn Sie Query-Optionen in useQuery einfügen, erhalten Sie automatische Typinferenz. Möglicherweise möchten Sie die Query-Optionen jedoch in eine separate Funktion extrahieren, um sie zwischen useQuery und z. B. prefetchQuery zu teilen. In diesem Fall würden Sie die Typinferenz verlieren. Um sie wiederherzustellen, können Sie den queryOptions-Helfer verwenden

ts
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())

Darüber hinaus kennt der von queryOptions zurückgegebene queryKey die zugehörige queryFn, und wir können diese Typinformationen nutzen, um Funktionen wie queryClient.getQueryData ebenfalls typsicher zu machen

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

Ohne queryOptions wäre der Typ von data unknown, es sei denn, wir würden ihm ein Generic übergeben

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

Typing Mutation Options

Ähnlich wie bei queryOptions können Sie mutationOptions verwenden, um Mutationsoptionen in eine separate Funktion zu extrahieren.

ts
function groupMutationOptions() {
  return mutationOptions({
    mutationKey: ['addGroup'],
    mutationFn: addGroup,
  })
}

useMutation({
  ...groupMutationOptions(),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['groups'] }),
})
useIsMutating(groupMutationOptions())
queryClient.isMutating(groupMutationOptions())
function groupMutationOptions() {
  return mutationOptions({
    mutationKey: ['addGroup'],
    mutationFn: addGroup,
  })
}

useMutation({
  ...groupMutationOptions(),
  onSuccess: () => queryClient.invalidateQueries({ queryKey: ['groups'] }),
})
useIsMutating(groupMutationOptions())
queryClient.isMutating(groupMutationOptions())

Typsicheres Deaktivieren von Abfragen mit skipToken

Wenn Sie TypeScript verwenden, können Sie skipToken verwenden, um eine Abfrage zu deaktivieren. Dies ist nützlich, wenn Sie eine Abfrage bedingt deaktivieren möchten, aber die Abfrage dennoch typsicher halten möchten. Lesen Sie mehr darüber im Leitfaden Disabling Queries.

Weitere Lektüre

Tipps und Tricks zur Typinferenz finden Sie unter React Query und TypeScript in den Community-Ressourcen. Um herauszufinden, wie Sie die bestmögliche Typsicherheit erreichen können, lesen Sie Type-safe React Query. The Query Options API erläutert, wie die Typinferenz mit der queryOptions-Hilfsfunktion funktioniert.