1. 타입스크립트 설정
React Query는 라이브러리와 프로젝트가 타입이 안전한지 확인하기 위해 TypeScript로 작성됨
명심해야 할 사항
- 현재(2023.12.12) TypeScript v4.7 이상을 사용해야 함
1.1 Type Inference(타입 추론)
React Query의 타입은 보통 잘 전달되므로, 타입 어노테이션을 직접 제공할 필요가 없다.
1const { data } = useQuery({2// ^? const data: number | undefined3queryKey: ['test'],4queryFn: () => Promise.resolve(5),5})
1const { data } = useQuery({2// ^? const data: string | undefined3queryKey: ['test'],4queryFn: () => Promise.resolve(5),5select: data => data.toString(),6})
아래 ts playground로 확인해봐도 된다. typescript playground
물론 queryFn을 잘 정의한 경우, 가장 잘 작동한다.
다만 명심할 것은 대부분의 fetching 라이브러리들은 기본적으로 any 타입을 반환한다.
그래서 타입이 잘 정의된 함수인지 확신이 있어야 한다.
1const fetchGroups = (): Promise<Group[]> => axios.get('/groups').then(response => response.data)23const { data } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })4// ^? const data: Group[] | undefined
1.2 Type Narrowing(타입을 특정 타입으로 좁혀서 정의)
React Query는 쿼리 결과에 대해 구별된 union(공용체) 유형을 사용한다.
이는 상태(status) 필드와 boolean 타입의 flag로 구분된다.
- e.g. 데이터를 정의하기 위해,
success 상태인지 체크함
1const { data, isSuccess } = useQuery({2queryKey: ['test'],3queryFn: () => Promise.resolve(5),4})56if (isSuccess) {7data8// ^? const data: number9}
1.2 Typing the error field(오류 필드 입력)
오류 유형의 기본값은 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와 같은 하위 클래스가 있는 경우,
오류 필드를 구체적으로 지정하기 위해 Type Narrowing을 사용할 수 있다.
1import axios from 'axios'23const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })4// ^? const error: Error | null56if (axios.isAxiosError(error)) {7error8// ^? const error: AxiosError9}
1.3 글로벌 에러 등록(registering)
TanStack Query v5를 사용하면 모든 것에 대해 글로벌 오류 유형을 설정할 수 있습니다. (^콜 사이드(call-sides)에 제네릭을 정의할 필요 없이,) (^Register 인터페이스를 수정해서),
이렇게 하면 타입추론이 계속 동직하지만, 오류 필드는 정의한 타입이 된다.
1import '@tanstack/react-query'23declare module '@tanstack/react-query' {4interface Register {5defaultError: AxiosError6}7}89const { error } = useQuery({ queryKey: ['groups'], queryFn: fetchGroups })10// ^? const error: AxiosError | null
1.4 메타 입력 : 글로벌 Meta 등록하기
글로벌 에러 타입을 등록하는 것과 마찬가지로 글로벌 Meta 유형도 등록할 수 있습니다.
이를 통해 queries 및 mutations에 대한 옵션 meta 필드가 일관되고 타입이 안전해진다.
1import '@tanstack/react-query'23declare module '@tanstack/react-query' {4interface Register {5queryMeta: MyMeta6mutationMeta: MyMeta7}8}
1.5 queries 옵션 입력
useQuery에 인라인 query 옵션을 입력하면, 알아서 타입 추론해준다. 그러나 당신은 별도의 함수 안에 query 옵션을 추출하는 걸 원할지 지도 모른다. (useQuery와 e.g. prefetchQuery와 같은 것들을 공유하기 위해,) 이 경우 타입 추론이 사라진다. 이를 되돌릴려면, queryOptions 도우미를 사용할 수 있다:
1import { queryOptions } from '@tanstack/react-query'23function groupOptions() {4return queryOptions({5queryKey: ['groups'],6queryFn: fetchGroups,7staleTime: 5 * 1000,8})9}1011useQuery(groupOptions())12queryClient.prefetchQuery(groupOptions())
나아가, queryOptions에서 반환된 queryKey는 관련된 queryFn에 대해 알고 있으며,
우리는 queryClient와 같은 함수들의 타입 정보에 영향을 줄 수 있다.
getQueryData도 이러한 타입들을 인식할 수 있다:
1function groupOptions() {2return queryOptions({3queryKey: ['groups'],4queryFn: fetchGroups,5staleTime: 5 * 1000,6})7}89const data = queryClient.getQueryData(groupOptions().queryKey)10// ^? const data: Group[] | undefined
queryOption과 상관없이, 데이터 타입은 unknown이다.
(,우리가 제너릭을 통과하지 않는 한:)
1const data = queryClient.getQueryData<Group[]>(['groups'])