Narrowing function return type
Hi, consider the toy example below: passing
true
as an argument causes the function to return a number
while passing false
causes it to return a boolean
. The inferred return type is however always number | boolean
regardless of what argument is passed.
I guess this is just a quirk with TS not narrowing function signature based on arguments passed. Is it possible to achieve this however in an elegant way? 🙂
6 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
Yeah taking out all the convex parts, I'm not sure how to get TS to do this sort of type narrowing for you.
Playing around a little bit, I could only get this behavior by defining an overload.
Let's see if discord will let me send this as a pretty link
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
You can also use generics, but I like Sarah's approach (overloads) better. However, both approaches aren't really type-safe in the sense that they don't enforce the type-level logic that you'd expect/want (playground)
Regarding Convex specifically though (and honestly also in normal TypeScript code), I would recommend just splitting this into two different functions.
It may not even end up being more code when you take into account the fancy TS type-level stuff you would need to do to get it to work with one, and it's safer and easier to understand
Thanks both of you! Haha yeah seems like quite a bit of hoop-jumping and arm-twisting has to be done to achieve this, and that you'll probably end up with even less peace of mind!! I think we'll end up writing two dedicated queries to get the intellisense and type safety in the frontend that we expect.
If anyone's curious as to why I was interested in this, it was for basically this logic:
getUnreadNotifications(userId, onlyReturnCount)
Two Convex queries sounds useful to me, remember you can write helpers functions so there's no code duplication
The generated types https://docs.convex.dev/client/open-api for Convex functions can't include overloads or type parameters, in some languages these would not be expressible. The goal is that a Convex Query should be an API endpoint that can be expressed in any language; even though the TypeScript integration is good, you're defining an API and it's nice for that to return a consistent type.
I'm curious if you can do
export const returnNumOrBool = authQuery(...) as RegisteredQuery<"public", { returnNumber: true }, number> | RegisteredQuery<"public", { returnNumber: false }, boolean>