#Type error: implicitly has type 'any' because it does not have a type annotation

43 messages · Page 1 of 1 (latest)

barren stag
#

Hello, I have a type error but can't figure it out why its causing it.

export const createInvoice = action({
  args: {
    orderId: v.id('orders'),
  },
  handler: async (ctx, args) => {
     // ... irrelevant code

    const order = await ctx.runQuery(internal.orders.getOrderByIdInternal, {
      orderId: args.orderId,
    })

    if (!order) {
      throw new Error('Order not found')
    }

    return order
  },
})

When I return the order, the variable order, createInvoice and handler turns red with a type error. (image attached).

'order' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)
'handler' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions.ts(7023)
'createInvoice' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.ts(7022)

What am I doing wrong?

earnest wigeonBOT
#

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!

wise lynx
barren stag
#

This is the internal.orders.getOrderByIdInternal internalQuery function.

export const getOrderByIdInternal = internalQuery({
  args: {
    orderId: v.id('orders'),
  },
  handler: async (ctx, args) => {
    const order = await ctx.db.get(args.orderId)
    if (!order) {
      return null
    }
    const orderLines = await ctx.db
      .query('order_lines')
      .withIndex('by_orderId', (q) => q.eq('orderId', args.orderId))
      .collect()
    const customer = await ctx.db.get(order.customerId)

    return {
      ...order,
      customer: {
        ...customer,
        address: customer?.addressId
          ? await ctx.db.get(customer.addressId)
          : null,
      },
      paymentForm: order.paymentFormId
        ? await ctx.db.get(order.paymentFormId)
        : null,
      paymentMethod: order.paymentMethodId
        ? await ctx.db.get(order.paymentMethodId)
        : null,
      use: order.useId ? await ctx.db.get(order.useId) : null,
      orderLines: await Promise.all(
        orderLines.map(async (orderLine) => {
          const product = await ctx.db.get(orderLine.productId)
          const productTaxesIds = product?.taxes
          const productTaxes = await Promise.all(
            productTaxesIds?.map(async (taxId) => {
              return await ctx.db.get(taxId)
            }) ?? [],
          )

          return {
            ...orderLine,
            ...(product ?? {}),
            productCategory: product?.productCategoryId
              ? await ctx.db.get(product.productCategoryId)
              : null,
            taxes: productTaxes,
            invoice: order.invoiceId ? await ctx.db.get(order.invoiceId) : null,
          }
        }),
      ),
    }
  },
})
barren stag
wise lynx
#

i don't know if there's a better way than annotating the return type. your type does look complex, that's a bummer. maybe @marsh prawn has a solution?

barren stag
#

@wise lynx @marsh prawn I create a "function return type" from my internal function, and the type is working, but the error still exists.

#
export type OrderInternal = Awaited<ReturnType<typeof getOrderByIdInternal>>

export const getOrderByIdInternal = internalQuery({
  args: {
    orderId: v.id('orders'),
  },
  handler: async (ctx, args) => {
    const order = await ctx.db.get(args.orderId)
    if (!order) {
      return null
    }
  // ...rest of the code
  return {
    // ...big object
  }
#

This is how I'm using it, but if I return the order, I got an error in the function variable and in handler property.

const order: OrderInternal = await ctx.runQuery(
  internal.orders.getOrderByIdInternal,
  {
    orderId: args.orderId,
  },
)

return order
marsh prawn
barren stag
#

I'm trying to returning a value in an action that depends on the result of calling ctx.runQuery or ctx.runMutation.

#

As Lee point out this link

marsh prawn
#

When you say the error still exists, could you show a screenshot of the error? There are a few ways it could manifest. (re "got an error in the function variable and in handler property.")

Big picture we need to make this more convenient, and some changes to the way we declare functions may help.

#

But it's going to be a while for big changes like that, until then we need clear workarounds. The general category of declaring types is usually enough, but that's vague, there are several places types could be declared

languid juniper
# barren stag This is how I'm using it, but if I return the order, I got an error in the funct...

The type you're providing for order is superfluous, it's the return type of the query you're running from inside the mutation so it's still circular inference.

You'll have to actually type out the object in this case without referencing the query that's creating the object. I've found in many cases like this that I don't actually need the whole object I'm returning, and only need a few keys. If that happens to be the case the return value will be simpler to type. But either way, you have to provide an independent type for any part of that query reponse that goes into your action response.

barren stag
barren stag
#

@languid juniper

languid juniper
#

Are there any other type errors in your Convex code?

#

Especially inference errors like this one

barren stag
#

No, those are the only ones @languid juniper

languid juniper
languid juniper
#

I am seeing that simply typing it is not enough, it gets tricky when the query return value is complex.

#

Or, to rephrase, typing is enough, but how to do that correctly gets tricky, and the compiler is often unable to help due to the inference issue.

barren stag
#

Thank you @languid juniper

languid juniper
#

Oh, better yet, can it return two different types?

#

Yeah it looks like a union return type on the query would still break things, even with accurate typing.

barren stag
#

Yes it breaks

#

It does not like returning the result of any runMutation, runQuery or runAction. Or better said, to depend on the returning result of those.

languid juniper
#

Minimal repro:

export const testQuery = query({
  handler: async (ctx) => {
    if ("".length) {
      return 42;
    }
    return "bar";
  },
});

export const testAction = action({
  // 'handler' implicitly has return type 'any' because it
  // does not have a return type annotation and is referenced
  // directly or indirectly in one of its return expressions.
  // ts(7023)
  handler: async (ctx) => {
    const value: number | string = await ctx.runQuery(
      api.app.testQuery,
    );
    return value
  },
});
languid juniper
#

Yep, I don't know how to work around it in the repro I just gave. Still trying though.

barren stag
#

👍

#

I think it is common to want to return something that depends on the result of runMutation/Query/Action

languid juniper
#

Ah, typing the return for the whole action, forgot about that:

export const testQuery = query({
  handler: async (ctx) => {
    if ("".length) {
      return 42;
    }
    return "bar";
  },
});

export const testAction = action({
  handler: async (ctx): Promise<number | string> => {
    const value = await ctx.runQuery(
      api.app.testQuery,
    );
    return value
  },
});
#

That ^^ has no errors

barren stag
#

So typing also the returned value

#

type*

languid juniper
#

If you type the return value of the action, you don't need to type the return value of runQuery