type wizard help needed for my fake trpc package

I kinda gave up on tRPC with app router cuz I got frustrated BUT I really like using tRPC. I appreciate that it's organized (but not too restrictive like a rest server) and the method by which inputs are passed and parsed. As such, I started working on a way in my app to define things almost exactly like a trpc router/procedure so that you still get input parsing with zod, type safety updates on definition and caller side, but just without the trpc stuff. At this point it's not even worth it, but I'm too invested and want it to work. This is majority of the code
type AsyncQuillQueryWithSchema<T> = (input: T) => Promise<unknown>;
type AsyncQuillQueryNoSchema = () => Promise<unknown>;

function createQuillRoute() {
const input = <T extends z.ZodTypeAny>(schema: T) => {
// define a fn that takes a query fn and returns a fn that parses it's input first
const typeSafeQueryInvoker = (
fn: AsyncQuillQueryWithSchema<z.infer<T>>,
) => {
return async (input: z.infer<typeof schema>) => {
try {
await schema.parseAsync(input);
return await fn(input);
} catch (error) {
throw error;
}
};
};

// return an object on that which is call-able
return {
query: typeSafeQueryInvoker,
// mutation: typeSafeMutationInvoker,
};
};

// we also want to define a query fn that doesn't take any input
const query = (fn: AsyncQuillQueryNoSchema) => {
return async () => fn();
};

return {
input,
query,
};
}

export const quill = () => ({
...createQuillRoute(),
});
type AsyncQuillQueryWithSchema<T> = (input: T) => Promise<unknown>;
type AsyncQuillQueryNoSchema = () => Promise<unknown>;

function createQuillRoute() {
const input = <T extends z.ZodTypeAny>(schema: T) => {
// define a fn that takes a query fn and returns a fn that parses it's input first
const typeSafeQueryInvoker = (
fn: AsyncQuillQueryWithSchema<z.infer<T>>,
) => {
return async (input: z.infer<typeof schema>) => {
try {
await schema.parseAsync(input);
return await fn(input);
} catch (error) {
throw error;
}
};
};

// return an object on that which is call-able
return {
query: typeSafeQueryInvoker,
// mutation: typeSafeMutationInvoker,
};
};

// we also want to define a query fn that doesn't take any input
const query = (fn: AsyncQuillQueryNoSchema) => {
return async () => fn();
};

return {
input,
query,
};
}

export const quill = () => ({
...createQuillRoute(),
});
which can then be used like this (where i nest it a few times)
const profile = await quillApi.user.getUserByProfileId({
username: props.params.username,
});
const profile = await quillApi.user.getUserByProfileId({
username: props.params.username,
});
this works surprisingly well, however, it's not inferring return type properly. I just get unknown, which makes sense since my fn types define the return as such. Anyone have thoughts?
1 Reply
jack
jackOP13mo ago
chatgpt gave me a working solution that creates some sort of UnpackPromise type and then casts with as. Not sure if this is optimal though, definitely looking for feedback
Want results from more Discord servers?
Add your server