style
style2mo ago

'getLogoUrl' implicitly has type 'any' because it does not have a type annotation and is referenced

When I use something like this (return internal query directly) then I get: 'getLogoUrl' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022) why is that? Shouldn't it infer type?
/**
* Get a signed URL for the project's logo asset, if present
*/
export const getLogoUrl = query({
args: { projectId: v.id('projects') },
returns: v.union(v.string(), v.null()),
handler: async (ctx, { projectId }) => {
const context = await getCurrentProjectContext(ctx, projectId)
if (!context.logoAsset) return null
return await ctx.runQuery(internal.projects.projectContext._internalGetLogoUrl, { logoAsset: context.logoAsset })
},
})

export const _internalGetLogoUrl = internalQuery({
args: { logoAsset: v.id('_storage') },
returns: v.union(v.string(), v.null()),
handler: async (ctx, { logoAsset }) => {
const url = await ctx.storage.getUrl(logoAsset)
return url ?? null
},
})
/**
* Get a signed URL for the project's logo asset, if present
*/
export const getLogoUrl = query({
args: { projectId: v.id('projects') },
returns: v.union(v.string(), v.null()),
handler: async (ctx, { projectId }) => {
const context = await getCurrentProjectContext(ctx, projectId)
if (!context.logoAsset) return null
return await ctx.runQuery(internal.projects.projectContext._internalGetLogoUrl, { logoAsset: context.logoAsset })
},
})

export const _internalGetLogoUrl = internalQuery({
args: { logoAsset: v.id('_storage') },
returns: v.union(v.string(), v.null()),
handler: async (ctx, { logoAsset }) => {
const url = await ctx.storage.getUrl(logoAsset)
return url ?? null
},
})
3 Replies
Convex Bot
Convex Bot2mo ago
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!
Clever Tagline
Clever Tagline2mo ago
I ran into this same issue yesterday with a couple of actions, where I wanted one to call the other to get some data. I think somewhere in the docs (or maybe a Stack article) there's probably a deeper explanation behind why the types go wonky, but I wasn't able to find it. I fixed it by turning the target action into a helper function. In my case, the solution looked something like this:
export const callingAction = action({
args: { /* args */},
handler: async (ctx, args) => {
// Call the other one to get some data
const data = await otherOne(ctx)
// ...keep going from there
}
})

async function otherOne(ctx: GenericActionCtx<any>) {
// Use the passed context as needed
const stuff = await ctx.blahblah
return stuff
}
export const callingAction = action({
args: { /* args */},
handler: async (ctx, args) => {
// Call the other one to get some data
const data = await otherOne(ctx)
// ...keep going from there
}
})

async function otherOne(ctx: GenericActionCtx<any>) {
// Use the passed context as needed
const stuff = await ctx.blahblah
return stuff
}
In your case, you could turn _internalGetLogoUrl into a function that accepts the ctx from the calling query typed as QueryCtx. As the docs indicate, all helper functions will not be callable from outside of your Convex functions, so they're purely private.
Queries | Convex Developer Hub
Fetch data from the database with caching and reactivity
style
styleOP2mo ago
I see, I can also type return value of handler. I think I'd prefer to stick with internal functions as I use them in multiple services to overcome safe guards, but thank you for idea!

Did you find this page helpful?