본문으로 건너뛰기
버전: 11.x

타입 추론

@trpc/server(여기 참조)를 통해 제공되는 타입 추론 외에도, 이 통합은 React에서 순수하게 사용하기 위한 몇 가지 추론 헬퍼를 제공합니다.

라우터 기반 React Query 옵션 추론

tRPC 프로시저를 감싸는 커스텀 훅을 만들 때, 옵션의 타입을 라우터에서 추론할 필요가 있는 경우가 있습니다. @trpc/react-query에서 내보낸 inferReactQueryProcedureOptions 헬퍼를 통해 이를 수행할 수 있습니다.

trpc.ts
ts
import {
createTRPCReact,
type inferReactQueryProcedureOptions,
} from '@trpc/react-query';
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server';
 
// infer the types for your router
export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;
 
export const trpc = createTRPCReact<AppRouter>();
trpc.ts
ts
import {
createTRPCReact,
type inferReactQueryProcedureOptions,
} from '@trpc/react-query';
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server';
import type { AppRouter } from './server';
 
// infer the types for your router
export type ReactQueryOptions = inferReactQueryProcedureOptions<AppRouter>;
export type RouterInputs = inferRouterInputs<AppRouter>;
export type RouterOutputs = inferRouterOutputs<AppRouter>;
 
export const trpc = createTRPCReact<AppRouter>();
usePostCreate.ts
ts
import {
trpc,
type ReactQueryOptions,
type RouterInputs,
type RouterOutputs,
} from './trpc';
 
type PostCreateOptions = ReactQueryOptions['post']['create'];
 
function usePostCreate(options?: PostCreateOptions) {
const utils = trpc.useUtils();
 
return trpc.post.create.useMutation({
...options,
onSuccess(post, variables, onMutateResult, context) {
// invalidate all queries on the post router
// when a new post is created
utils.post.invalidate();
options?.onSuccess?.(post, variables, onMutateResult, context);
},
});
}
usePostCreate.ts
ts
import {
trpc,
type ReactQueryOptions,
type RouterInputs,
type RouterOutputs,
} from './trpc';
 
type PostCreateOptions = ReactQueryOptions['post']['create'];
 
function usePostCreate(options?: PostCreateOptions) {
const utils = trpc.useUtils();
 
return trpc.post.create.useMutation({
...options,
onSuccess(post, variables, onMutateResult, context) {
// invalidate all queries on the post router
// when a new post is created
utils.post.invalidate();
options?.onSuccess?.(post, variables, onMutateResult, context);
},
});
}
usePostById.ts
ts
import { ReactQueryOptions, RouterInputs, trpc } from './trpc';
 
type PostByIdOptions = ReactQueryOptions['post']['byId'];
type PostByIdInput = RouterInputs['post']['byId'];
 
function usePostById(input: PostByIdInput, options?: PostByIdOptions) {
return trpc.post.byId.useQuery(input, options);
}
usePostById.ts
ts
import { ReactQueryOptions, RouterInputs, trpc } from './trpc';
 
type PostByIdOptions = ReactQueryOptions['post']['byId'];
type PostByIdInput = RouterInputs['post']['byId'];
 
function usePostById(input: PostByIdInput, options?: PostByIdOptions) {
return trpc.post.byId.useQuery(input, options);
}

"라우터 팩토리"에서 추상 타입 추론

애플리케이션에서 유사한 라우터 인터페이스를 여러 번 생성하는 팩토리를 작성하는 경우, 팩토리의 사용처 간에 클라이언트 코드를 공유하기를 원할 수 있습니다. @trpc/react-query/shared 패키지는 라우터 팩토리에 대한 추상 타입을 생성하고, 라우터를 prop으로 전달받아 사용하는 공통 React 컴포넌트를 구축하는 데 사용할 수 있는 여러 타입을 내보냅니다.

api/factory.ts
tsx
import { z } from 'zod';
import { t, publicProcedure } from './trpc';
 
// @trpc/react-query/shared exports several **Like types which can be used to generate abstract types
import { RouterLike, UtilsLike } from '@trpc/react-query/shared';
 
const ThingRequest = z.object({ name: z.string() });
const Thing = z.object({ id: z.string(), name: z.string() });
const ThingQuery = z.object({ filter: z.string().optional() });
const ThingArray = z.array(Thing);
 
// Factory function written by you, however you need,
// so long as you can infer the resulting type of t.router() later
export function createMyRouter() {
return t.router({
createThing: publicProcedure
.input(ThingRequest)
.output(Thing)
.mutation(({ input }) => ({ id: '1', ...input })),
listThings: publicProcedure
.input(ThingQuery)
.output(ThingArray)
.query(() => []),
})
}
 
// Infer the type of your router, and then generate the abstract types for use in the client
type MyRouterType = ReturnType<typeof createMyRouter>
export type MyRouterLike = RouterLike<MyRouterType>
export type MyRouterUtilsLike = UtilsLike<MyRouterType>
api/factory.ts
tsx
import { z } from 'zod';
import { t, publicProcedure } from './trpc';
 
// @trpc/react-query/shared exports several **Like types which can be used to generate abstract types
import { RouterLike, UtilsLike } from '@trpc/react-query/shared';
 
const ThingRequest = z.object({ name: z.string() });
const Thing = z.object({ id: z.string(), name: z.string() });
const ThingQuery = z.object({ filter: z.string().optional() });
const ThingArray = z.array(Thing);
 
// Factory function written by you, however you need,
// so long as you can infer the resulting type of t.router() later
export function createMyRouter() {
return t.router({
createThing: publicProcedure
.input(ThingRequest)
.output(Thing)
.mutation(({ input }) => ({ id: '1', ...input })),
listThings: publicProcedure
.input(ThingQuery)
.output(ThingArray)
.query(() => []),
})
}
 
// Infer the type of your router, and then generate the abstract types for use in the client
type MyRouterType = ReturnType<typeof createMyRouter>
export type MyRouterLike = RouterLike<MyRouterType>
export type MyRouterUtilsLike = UtilsLike<MyRouterType>
api/server.ts
tsx
export type AppRouter = typeof appRouter;
 
// Export your MyRouter types to the client
export type { MyRouterLike, MyRouterUtilsLike } from './factory';
api/server.ts
tsx
export type AppRouter = typeof appRouter;
 
// Export your MyRouter types to the client
export type { MyRouterLike, MyRouterUtilsLike } from './factory';
frontend/usePostCreate.ts
tsx
import type { MyRouterLike, MyRouterUtilsLike } from './factory';
 
type MyGenericComponentProps = {
route: MyRouterLike;
utils: MyRouterUtilsLike;
};
 
function MyGenericComponent(props: MyGenericComponentProps) {
const { route } = props;
const thing = route.listThings.useQuery({
filter: 'qwerty',
});
 
const mutation = route.doThing.useMutation({
onSuccess() {
props.utils.listThings.invalidate();
},
});
 
function handleClick() {
mutation.mutate({
name: 'Thing 1',
});
}
 
return null; /* ui */
}
 
function MyPageComponent() {
const utils = useUtils();
 
return (
<MyGenericComponent
route={trpc.deep.route.things}
utils={utils.deep.route.things}
/>
);
}
 
function MyOtherPageComponent() {
const utils = useUtils();
 
return (
<MyGenericComponent
route={trpc.different.things}
utils={utils.different.things}
/>
);
}
frontend/usePostCreate.ts
tsx
import type { MyRouterLike, MyRouterUtilsLike } from './factory';
 
type MyGenericComponentProps = {
route: MyRouterLike;
utils: MyRouterUtilsLike;
};
 
function MyGenericComponent(props: MyGenericComponentProps) {
const { route } = props;
const thing = route.listThings.useQuery({
filter: 'qwerty',
});
 
const mutation = route.doThing.useMutation({
onSuccess() {
props.utils.listThings.invalidate();
},
});
 
function handleClick() {
mutation.mutate({
name: 'Thing 1',
});
}
 
return null; /* ui */
}
 
function MyPageComponent() {
const utils = useUtils();
 
return (
<MyGenericComponent
route={trpc.deep.route.things}
utils={utils.deep.route.things}
/>
);
}
 
function MyOtherPageComponent() {
const utils = useUtils();
 
return (
<MyGenericComponent
route={trpc.different.things}
utils={utils.different.things}
/>
);
}

더 완전한 동작 예제는 여기에서 찾을 수 있습니다.