eslint: no-filter-in-query misses any-typed receivers with parser services

Author: JohanRGustafssonCreated Sep 2, 2026Updated Sep 6, 2026

Bug

@convex-dev/no-filter-in-query misses a syntactically recognizable Convex query when parser services are enabled and the database receiver is typed as any.

The same call is reported when type-aware linting is disabled. Enabling type-aware linting therefore removes coverage for this shape.

Minimal reproduction

typescript
type Ctx = {
  db: any;
};

async function findTodo(ctx: Ctx) {
  return await ctx.db
    .query("todoItems")
    .withIndex("by_file", (q: any) => q.eq("fileId", "file-id"))
    .filter((q: any) => q.eq(q.field("status"), "open"))
    .first();
}

Run ESLint once with the TypeScript parser and the rule, then again with the same config plus parserOptions.projectService: true.

  • Without projectService, the syntax fallback reports no-filter-in-query.
  • With projectService, the rule reports nothing.

Reproduced with:

  • @convex-dev/eslint-plugin 2.0.0
  • ESLint 9.39.4
  • typescript-eslint 8.62.0
  • TypeScript 6.0.3
  • Convex 1.45.0
  • Windows

Cause

In the type-aware path, both isConvexQueryFilterCall() and the receiver-type check return false because the call and receiver are any. hasUsefulReceiverType is also false. However, shouldUseAstFallback starts as !hasTypeInfo, so it remains false whenever parser services exist. The syntax fallback is skipped even though type information was inconclusive.

The relevant flow is:

typescript
let shouldUseAstFallback = !hasTypeInfo;
// receiverTypeString === "any"
const hasUsefulReceiverType = false;
// shouldUseAstFallback remains false

The likely fix is to enable the syntax fallback when the receiver type is any or unknown, while retaining the existing terminal-call guard that prevents array .filter() false positives.

Impact

In one project, a no-cache comparison over the same 23 Convex query filters found 23 reports without type information and 20 with type information. The missing three all used an any database context. Typing that context as Pick<MutationCtx, "db" | "scheduler" | "runMutation"> restored all three reports.

Source: get-convex/convex-backend