TypeScript not Inferring Query `handler` Type Correctly
Hey everyone, I'm trying to "JOIN" some data in my query in order to include the creator directly in my response. However, it seems like TypeScript isn't able to properly infer the handler's return type even with excessive type hints:
The error I get from TypeScript is:
Can someone assist in this on how I can best JOIN the creator?
export const find = query({
args: {
paginationOpts: paginationOptsValidator,
filter: v.optional(
v.object({
createdById: v.optional(v.id("users")),
search: v.optional(v.string()),
})
),
},
handler: async (ctx, args) => {
let query = ctx.db.query("protests");
if (args.filter?.search) {
return await query
.withSearchIndex("fts", (q) => q.search("fts", args.filter!.search!))
.paginate(args.paginationOpts);
}
if (args.filter?.createdById) {
query = query.filter((q) =>
q.eq(q.field("createdById"), args.filter?.createdById)
);
}
const { page, ...res } = await query.paginate(args.paginationOpts);
const users: (Doc<"users"> & {
profileImageUrl?: string;
})[] = await Promise.all(
page
.reduce(
(ids, protest) =>
ids.indexOf(protest.createdById) !== -1
? ids
: [...ids, protest.createdById],
[] as Id<"users">[]
)
.map(
(id) =>
ctx.runQuery(api.users.findById, { id }) as Promise<
Doc<"users"> & {
profileImageUrl?: string;
}
>
)
);
return {
...res,
page: page.map(
(protest) =>
({
...protest,
createdBy: users.find((u) => u?._id === protest.createdById),
}) as Doc<"protests"> & {
createdBy: Doc<"users"> & {
profileImageUrl?: string;
};
}
),
};
},
returns: v.object({
isDone: v.boolean(),
continueCursor: v.string(),
splitCursor: v.optional(v.union(v.string(), v.null())),
pageStatus: v.optional(
v.union(
v.literal("SplitRecommended"),
v.literal("SplitRequired"),
v.null()
)
),
page: v.array(
v.object({
_id: v.id("protests"),
_creationTime: v.number(),
name: v.string(),
startDate: v.number(),
endDate: v.optional(v.number()),
createdById: v.id("users"),
mainLocationId: v.optional(v.id("locations")),
missionStatement: v.string(),
fts: v.string(),
createdBy: v.object({
name: v.optional(v.string()),
image: v.optional(v.string()),
email: v.optional(v.string()),
emailVerificationTime: v.optional(v.number()),
phone: v.optional(v.string()),
phoneVerificationTime: v.optional(v.number()),
isAnonymous: v.optional(v.boolean()),
profileImageId: v.optional(v.id("_storage")),
profileImageUrl: v.optional(v.string()),
}),
})
),
}),
});export const find = query({
args: {
paginationOpts: paginationOptsValidator,
filter: v.optional(
v.object({
createdById: v.optional(v.id("users")),
search: v.optional(v.string()),
})
),
},
handler: async (ctx, args) => {
let query = ctx.db.query("protests");
if (args.filter?.search) {
return await query
.withSearchIndex("fts", (q) => q.search("fts", args.filter!.search!))
.paginate(args.paginationOpts);
}
if (args.filter?.createdById) {
query = query.filter((q) =>
q.eq(q.field("createdById"), args.filter?.createdById)
);
}
const { page, ...res } = await query.paginate(args.paginationOpts);
const users: (Doc<"users"> & {
profileImageUrl?: string;
})[] = await Promise.all(
page
.reduce(
(ids, protest) =>
ids.indexOf(protest.createdById) !== -1
? ids
: [...ids, protest.createdById],
[] as Id<"users">[]
)
.map(
(id) =>
ctx.runQuery(api.users.findById, { id }) as Promise<
Doc<"users"> & {
profileImageUrl?: string;
}
>
)
);
return {
...res,
page: page.map(
(protest) =>
({
...protest,
createdBy: users.find((u) => u?._id === protest.createdById),
}) as Doc<"protests"> & {
createdBy: Doc<"users"> & {
profileImageUrl?: string;
};
}
),
};
},
returns: v.object({
isDone: v.boolean(),
continueCursor: v.string(),
splitCursor: v.optional(v.union(v.string(), v.null())),
pageStatus: v.optional(
v.union(
v.literal("SplitRecommended"),
v.literal("SplitRequired"),
v.null()
)
),
page: v.array(
v.object({
_id: v.id("protests"),
_creationTime: v.number(),
name: v.string(),
startDate: v.number(),
endDate: v.optional(v.number()),
createdById: v.id("users"),
mainLocationId: v.optional(v.id("locations")),
missionStatement: v.string(),
fts: v.string(),
createdBy: v.object({
name: v.optional(v.string()),
image: v.optional(v.string()),
email: v.optional(v.string()),
emailVerificationTime: v.optional(v.number()),
phone: v.optional(v.string()),
phoneVerificationTime: v.optional(v.number()),
isAnonymous: v.optional(v.boolean()),
profileImageId: v.optional(v.id("_storage")),
profileImageUrl: v.optional(v.string()),
}),
})
),
}),
});The error I get from TypeScript is:
Type 'Promise<PaginationResult<...>>' is not assignable to type 'ValidatorTypeToReturnType<{ splitCursor?: string | null | undefined; pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined; isDone: boolean; continueCursor: string; page: { endDate?: number | undefined; ... 8 more ...; createdBy: { ...; }; }[]; }>'.
...
Property 'createdBy' is missing in type '{ _id: Id<"protests">; _creationTime: number; endDate?: number | undefined; mainLocationId?: Id<"locations"> | undefined; name: string; startDate: number; createdById: Id<"users">; missionStatement: string; fts: string; }' but required in type '{ endDate?: number | undefined; mainLocationId?: Id<"locations"> | undefined; name: string; _creationTime: number; startDate: number; createdById: Id<"users">; missionStatement: string; fts: string; _id: Id<...>; createdBy: { ...; }; }'.ts(2719)Type 'Promise<PaginationResult<...>>' is not assignable to type 'ValidatorTypeToReturnType<{ splitCursor?: string | null | undefined; pageStatus?: "SplitRecommended" | "SplitRequired" | null | undefined; isDone: boolean; continueCursor: string; page: { endDate?: number | undefined; ... 8 more ...; createdBy: { ...; }; }[]; }>'.
...
Property 'createdBy' is missing in type '{ _id: Id<"protests">; _creationTime: number; endDate?: number | undefined; mainLocationId?: Id<"locations"> | undefined; name: string; startDate: number; createdById: Id<"users">; missionStatement: string; fts: string; }' but required in type '{ endDate?: number | undefined; mainLocationId?: Id<"locations"> | undefined; name: string; _creationTime: number; startDate: number; createdById: Id<"users">; missionStatement: string; fts: string; _id: Id<...>; createdBy: { ...; }; }'.ts(2719)Can someone assist in this on how I can best JOIN the creator?
