Tom Redman
Tom Redman3mo ago

Action calling an action: "implicit any" error?

Hey all, @erquhart I feel like you'll know this, haha. I have the simplest action calling another action via ctx.runAction. It's throwing the classic "implicitly has type 'any' because..." error, but I'd really like to understand why. (For the record, I have "solved" this by using a helper function, per best practice, instead of an action calling an action. Regardless, I need to understand why this isn't working.) - I can also annotate it with as { message: ... }, but why would I need to do that when the return type is static and very simple? - Adding returns argument doesn't solve it either E.g.
returns: v.object({
message: v.string(),
}),

// and

returns: v.object({
simpleAction: v.object({
message: v.string(),
}),
}),
returns: v.object({
message: v.string(),
}),

// and

returns: v.object({
simpleAction: v.object({
message: v.string(),
}),
}),
Here's the whole setup:
import { action, internalAction } from "../_generated/server";
import { api } from "../_generated/api";

export const simpleAction = internalAction({
args: {},
handler: async (ctx, args) => {
return {
message: `Hello Test`,
};
},
});

export const simpleActionTest = action({
args: {},
handler: async (ctx, args) => {
const _simpleAction = await ctx.runAction(
api.authBridge.simpleAction.simpleAction,
{}
);
return {
simpleAction: _simpleAction,
};
},
});
import { action, internalAction } from "../_generated/server";
import { api } from "../_generated/api";

export const simpleAction = internalAction({
args: {},
handler: async (ctx, args) => {
return {
message: `Hello Test`,
};
},
});

export const simpleActionTest = action({
args: {},
handler: async (ctx, args) => {
const _simpleAction = await ctx.runAction(
api.authBridge.simpleAction.simpleAction,
{}
);
return {
simpleAction: _simpleAction,
};
},
});
Error in Terminal (note VS Code doesn't show any issues):
convex/authBridge/simpleAction.ts:16:11 - error TS7022: '_simpleAction' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

16 const _simpleAction = await ctx.runAction(
~~~~~~~~~~~~~

Found 3 errors in the same file, starting at: convex/authBridge/simpleAction.ts:13
convex/authBridge/simpleAction.ts:16:11 - error TS7022: '_simpleAction' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

16 const _simpleAction = await ctx.runAction(
~~~~~~~~~~~~~

Found 3 errors in the same file, starting at: convex/authBridge/simpleAction.ts:13
No description
2 Replies
erquhart
erquhart3mo ago
The expandable box breaks down the "why" pretty well here: https://docs.convex.dev/functions/actions#dealing-with-circular-type-inference tl;dr you have to type the return value of the function you're calling (at least the part you're returning from the outer function), or the return type of the outer function. So you can fix by doing:
export const simpleActionTest = action({
args: {},
handler: async (ctx, args) => {
// Type the return value of the sub action since
// you go on to return the whole thing
const _simpleAction: { message: string } = await ctx.runAction(
api.authBridge.simpleAction.simpleAction,
{}
);
return {
simpleAction: _simpleAction,
};
},
});
export const simpleActionTest = action({
args: {},
handler: async (ctx, args) => {
// Type the return value of the sub action since
// you go on to return the whole thing
const _simpleAction: { message: string } = await ctx.runAction(
api.authBridge.simpleAction.simpleAction,
{}
);
return {
simpleAction: _simpleAction,
};
},
});
Note that this isn't a type assertion - if you did message: number, for example, it would still be a type error. This is just keeping your outer function from relying on inference for the return value of the inner function, as this would be circular inference and isn't resolvable.
Tom Redman
Tom RedmanOP3mo ago
You are next level @erquhart

Did you find this page helpful?