TypeScript
이제 React Query는 라이브러리와 프로젝트의 타입 안전성을 보장하기 위해 TypeScript로 작성되었습니다!
유의할 사항:
- TanStack Query는 DefinitelyTyped의 지원 기간을 따르며, 최근 2년 이내에 출시된 TypeScript 버전을 지원합니다. 현재 이는 TypeScript 5.6 이상을 의미합니다.
- 이 repository의 type 변경은 호환성을 깨뜨리지 않는 변경으로 간주되며, 일반적으로 semver의 patch 변경으로 릴리스됩니다(그렇지 않으면 모든 type 개선이 major version이 될 것입니다!).
- react-query 패키지 버전을 특정 패치 릴리스로 고정하고, 어느 릴리스 사이에서든 타입이 수정되거나 업그레이드될 수 있음을 예상하면서 업그레이드하는 것을 강력히 권장합니다.
- React Query의 타입과 관련 없는 공개 API는 여전히 semver를 매우 엄격하게 따릅니다.
타입 추론
React Query의 타입은 일반적으로 매우 원활하게 전파되므로 직접 타입 어노테이션을 제공하지 않아도 됩니다
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(),
})
queryFn의 반환 타입이 명확하게 정의되어 있을 때 가장 효과적입니다. 대부분의 데이터 가져오기 라이브러리는 기본적으로 any를 반환하므로, 적절한 타입이 지정된 함수로 추출해야 합니다:
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
}
오류 필드 타입 지정하기
대부분의 사용자가 이를 예상하므로 오류 타입의 기본값은 Error입니다.
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error
사용자 지정 오류 또는 Error가 전혀 아닌 값을 발생시키려면 error 필드의 타입을 지정할 수 있습니다:
const { error } = useQuery<Group[], string>(['groups'], fetchGroups)
// ^? const error: string | null
하지만 이렇게 하면 useQuery의 다른 모든 제네릭에 대한 타입 추론이 더 이상 작동하지 않는다는 단점이 있습니다. 일반적으로 Error가 아닌 것을 발생시키는 것은 좋은 관행으로 간주되지 않으므로, AxiosError 같은 하위 클래스가 있다면 _타입 좁히기_를 사용하여 error 필드를 더 구체적으로 만들 수 있습니다:
import axios from 'axios'
const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })
// ^? const error: Error | null
if (axios.isAxiosError(error)) {
error
// ^? const error: AxiosError
}
전역 Error 등록하기
TanStack Query v5를 사용하면 Register 인터페이스를 수정하여 호출 지점에서 제네릭을 지정할 필요 없이 모든 항목에 전역 Error 타입을 설정할 수 있습니다. 이렇게 하면 추론이 계속 작동하면서 오류 필드는 지정된 타입을 갖게 됩니다. 호출 지점에서 명시적으로 타입을 좁히도록 강제하려면 defaultError를 unknown으로 설정합니다:
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
meta 타입 지정
전역 Meta 등록하기
전역 오류 타입을 등록하는 것과 마찬가지로 전역 Meta 타입도 등록할 수 있습니다. 이렇게 하면 쿼리와 뮤테이션의 선택적 meta 필드가 일관성과 타입 안전성을 유지합니다. meta가 객체로 유지되도록 등록된 타입은 Record<string, unknown>를 확장해야 합니다.
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
}
}
쿼리 및 뮤테이션 키 타입 지정
쿼리 및 뮤테이션 키 타입 등록
또한 전역 오류 타입을 등록하는 것과 마찬가지로 전역 QueryKey 및 MutationKey 타입도 등록할 수 있습니다. 이를 통해 애플리케이션의 계층 구조에 맞게 키에 더 많은 구조를 부여하고 라이브러리의 모든 영역에서 해당 타입이 적용되도록 할 수 있습니다. 키가 배열로 유지되도록 등록된 타입은 Array 타입을 확장해야 한다는 점에 유의하세요.
import '@tanstack/react-query'
type QueryKey = ['dashboard' | 'marketing', ...ReadonlyArray<unknown>]
declare module '@tanstack/react-query' {
interface Register {
queryKey: QueryKey
mutationKey: QueryKey
}
}
쿼리 옵션 타입 지정
쿼리 옵션을 useQuery에 인라인으로 작성하면 타입이 자동으로 추론됩니다. 하지만 쿼리 옵션을 별도 함수로 추출하여 useQuery 및 예를 들어 query 간에 공유하고 싶을 수 있습니다. 이 경우 타입 추론을 잃게 됩니다. 타입 추론을 되찾으려면 queryOptions 헬퍼를 사용할 수 있습니다:
import { queryOptions } from '@tanstack/react-query'
function groupOptions() {
return queryOptions({
queryKey: ['groups'],
queryFn: fetchGroups,
staleTime: 5 * 1000,
})
}
useQuery(groupOptions())
queryClient.query(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
queryOptions가 없으면 제네릭을 전달하지 않는 한 data의 타입은 unknown이 됩니다:
const data = queryClient.getQueryData<Group[]>(['groups'])
queryOptions를 통한 타입 추론은 queryClient.getQueriesData에서 작동하지 않습니다. 이 함수는 이질적인 unknown 데이터를 포함하는 튜플 배열을 반환하기 때문입니다. 쿼리가 반환할 데이터 타입을 확신한다면 명시적으로 지정하세요:
const entries = queryClient.getQueriesData<Group[]>(groupOptions().queryKey)
// ^? const entries: Array<[QueryKey, Group[] | undefined]>
Mutation 옵션 타입 지정
queryOptions와 마찬가지로 mutationOptions를 사용하여 뮤테이션 옵션을 별도의 함수로 추출할 수 있습니다:
function groupMutationOptions() {
return mutationOptions({
mutationKey: ['addGroup'],
mutationFn: addGroup,
})
}
useMutation({
...groupMutationOptions(),
onSuccess: () => queryClient.invalidateQueries({ queryKey: ['groups'] }),
})
useIsMutating(groupMutationOptions())
queryClient.isMutating(groupMutationOptions())
skipToken을 사용한 타입 안전 쿼리 비활성화
TypeScript를 사용하는 경우 skipToken을 사용하여 쿼리를 비활성화할 수 있습니다. 이는 조건에 따라 쿼리를 비활성화하면서도 쿼리의 타입 안전성을 유지하려는 경우에 유용합니다.
자세한 내용은 쿼리 비활성화 가이드를 참조하세요.
추가 자료
타입 추론에 관한 팁과 요령은 React Query 및 TypeScript 문서를 참조하세요. 가능한 최상의 타입 안전성을 확보하는 방법은 타입 안전 React Query에서 확인할 수 있습니다. Query Options API에서는 queryOptions 헬퍼 함수에서 타입 추론이 작동하는 방식을 설명합니다.