I have the following Vue 3 useTRPCQuery()
composable which lightly wraps tRPC.io's client.query() call, which aims to be type-aware and reactive.
My usage attempt is as follows:
export function useQuery(
client: TRPCClient<AppRouter>,
pathAndInput: [path: TPath, ...args: inferHandlerInput<TQueries[TPath]>]
) {
const response = ref<inferQueryOutput<TPath>>()
;(async () => {
const data = await client.query(*...pathAndInput*) # TS Error Here
response.value = data
})()
return response
}
All works ok at runtime, but I'm seeing the following error around the pathAndInput
tuple:
spread argument must either have a tuple type or be passed to a rest parameter.
I've looked at potential fixes for this, such as passing in path
and ...args
separately, e.g.,
export function useQuery(
client: TRPCClient<AppRouter>,
path: TPath,
...args: inferHandlerInput<TQueries[TPath]>
) {
const response = ref<inferQueryOutput<TPath>>()
;(async () => {
const data = await client.query(path, ...args)
response.value = data
})()
return response
}
but I'd love to be able to have my usage of this composable as follows:
export const useTelescopes = (client: TRPCClient<AppRouter>) => {
return {
telescopes: useQuery(client, ['telescope.all', someExtraArgs])
}
}
Is there something I am missing that can allow me to use a tuple in this way?
The full code for the useQuery reference is as follows:
import { ref } from 'vue'
import type {
ProcedureRecord,
inferHandlerInput,
inferProcedureInput,
inferProcedureOutput
} from '@trpc/server'
import type { TRPCClient, TRPCClientErrorLike } from '@trpc/client'
import type { AppRouter } from '../somewhere/where/your/server/is/server.ts'
export type TQueries = AppRouter['_def']['queries']
export type TError = TRPCClientErrorLike<AppRouter>
export type inferProcedures<TObj extends ProcedureRecord<any, any, any, any, any, any>> = {
[TPath in keyof TObj]: {
input: inferProcedureInput<TObj[TPath]>
output: inferProcedureOutput<TObj[TPath]>
}
}
export type TQueryValues = inferProcedures<AppRouter['_def']['queries']>
export type TPath = keyof TQueryValues & string
/**
*
* This is a helper method to infer the output of a query resolver
*
*/
export type inferQueryOutput<TRouteKey extends keyof AppRouter['_def']['queries']> = inferProcedureOutput<
AppRouter['_def']['queries'][TRouteKey]
>
/**
*
* This is a helper method to infer the input of a query resolver
*
*/
export type inferQueryInput<TRouteKey extends keyof AppRouter['_def']['queries']> = inferProcedureInput<
AppRouter['_def']['queries'][TRouteKey]
>
export function useQuery(
client: TRPCClient<AppRouter>,
path: TPath,
...args: inferHandlerInput<TQueries[TPath]>
) {
const response = ref<inferQueryOutput<TPath>>()
;(async () => {
const data = await client.query(path, ...args)
response.value = data
})()
return response
}
An example AppRouter type would be:
import * as trpc from '@trpc/server';
export const appRouter = trpc
.router<Context>()
// Create procedure at path 'hello'
.query('hello', {
resolve({ ctx }) {
return {
greeting: `hello world`,
};
},
});
type AppRouter = typeof appRouter;
which should be queryable as the following:
useQuery(client, ['hello'])