H
Hono3d ago
Eternal

How to get strict inference with RPC

Here's an example I found online https://x.com/honojs/status/1953608853654057157 In this example, the RPC infers the response as a "string" rather than the literal Hi!. I somewhat got around this by adding as const at the end of { message: "Hi!"} so it looked like { message: "Hi!"} as const but then I couldn't assign variables to the response of that API call because the API calls response is infered as readonly I fixed that by doing { message: "Hi!" as const} but is that the "proper" way to achieve strict inference? And why isn't there an example in the docs about this?
Hono (@honojs)
Hono v4.9 is out! `parseResponse` can parse Response from hc with type-safety https://t.co/IDfP4IfLt8
From Hono (@honojs)
X
14 Replies
Eternal
EternalOP3d ago
Please ping me when you respond
ambergristle
ambergristle3d ago
@Eternal wym by "couldn't assign variables to the response"? there are two ways of getting the rpc to recognize a literal string type:
// using `as const`, like you did
// can be either on string property or object as a whole
return c.json({ message: 'hi' } as const);
// or by typing the response
return c.json<{ message: 'hi' }>({ message: 'hi' })
// using `as const`, like you did
// can be either on string property or object as a whole
return c.json({ message: 'hi' } as const);
// or by typing the response
return c.json<{ message: 'hi' }>({ message: 'hi' })
Eternal
EternalOP3d ago
let data: string = "" const request = //insert request here const json = await request.json() data = json.data //error. TypeScript complains that json.data can't be assigned to the variable data because data is not readonly. Json.data gets infered as a readonly type Is there another way? The real way I'm using this in prod is I have a success boolean that's returned If that boolean is true, then the data key will also be returned ({success: true, data: whatever}) If it's false, the message key will be added ({success: false, message: string}) The reason I need "as const" is because I need the RPC to realize that the message key can only exist if success is false and I don't want to add the type to every possible c.json response because I will have to write a lot of types I prefer everything is infered
ambergristle
ambergristle3d ago
you might be better off using status codes rather than a boolean flag but the short answer is no, there isn't a lower-touch way to type the response like that any other solutions i can think of are just layers of abstraction on top of one of those two concepts that being said, i can't repro your TS error. can you share your tsconfig?
Eternal
EternalOP3d ago
I don't have a tsconfig I'm using Nuxt3 + hono RPC The data variable is actually "const data = ref("")" And the assignment is actually "data.value = json.data" I didn't include the details because I wanted to keep the general idea there and remove everything that wasn't necessary I can provide a screenshot of TS complaining tomorrow if you'd like
Eternal
EternalOP3d ago
I forgot it did that Do you want me to give you the TS config tomorrow?
ambergristle
ambergristle3d ago
yeah. it's funky that the type is getting inferred as a readonly but a screenshot of the error is also key
Eternal
EternalOP9h ago
I'll ping you when I hop on tomorrow, then Well, shoot I forgot to send the tsconfig and I don't have access to my computer right now Cheers to me not forgetting a 2nd time? I remembered! Here's the contents of ./.nuxt/tsconfig.json
Eternal
EternalOP9h ago
Client code:
const postsRequest = await postsRouteClient.posts.list.$get({ query: { type: filter.value.toLowerCase() } })
const postsRequestJson = await postsRequest.json()
const postsRequest = await postsRouteClient.posts.list.$get({ query: { type: filter.value.toLowerCase() } })
const postsRequestJson = await postsRequest.json()
Server code:
return c.json({ success: true, data: processedPosts } as const)
return c.json({ success: true, data: processedPosts } as const)
Infered type for postsRequestJson:
const postsRequestJson: {
readonly success: true;
readonly data: {
id: number;
title: string;
content: {
text: string;
images: string[];
};
actionReason: string | null;
actionByUserId: number | null;
actionDate: number | null;
type: "user" | "club";
tags: ("art" | "poem" | "other")[];
status: "PENDING_REVIEW" | "REJECTED" | "APPROVED" | "DELETED";
anonymous: boolean;
creationDate: number;
}[];
}
const postsRequestJson: {
readonly success: true;
readonly data: {
id: number;
title: string;
content: {
text: string;
images: string[];
};
actionReason: string | null;
actionByUserId: number | null;
actionDate: number | null;
type: "user" | "club";
tags: ("art" | "poem" | "other")[];
status: "PENDING_REVIEW" | "REJECTED" | "APPROVED" | "DELETED";
anonymous: boolean;
creationDate: number;
}[];
}
Screenshot of type is included (ignore the "no overlap" error)
No description
Eternal
EternalOP9h ago
@ambergristle ping!
ambergristle
ambergristle8h ago
@Eternal thanks! i'm seeing the readonly type on my local, but i can't repro an assignment type issue it would be helpful if you could share the type error when you're able, and maybe the snippet that's causing it
Eternal
EternalOP8h ago
I forgot to include that part I can't reproduce it now either what the heck

Did you find this page helpful?