Prince
Prince•8mo ago

Query type definition does not include all possible return scenarios

My query function is not returning all the possible values as a type definition (i.e. fullReportWithUser below), is this something I need to manually add with manual types or am I doing something wrong?
export const getReport = query({
args: { reportId: v.id("reports") },
handler: async (ctx, { reportId }) => {
const user = await currentUser(ctx, {})
const fullReport = await ctx.db.get(reportId)
if (!fullReport) return null
const { cost, recordingUrl, ...report } = fullReport
if (user?.admin) {
if (fullReport.userId) {
const reportUser = await ctx.db.get(fullReport.userId)
const fullReportWithUser = { ...fullReport, reportUser }
return fullReportWithUser
} else {
return fullReport
}
} else {
return report
}
},
})
export const getReport = query({
args: { reportId: v.id("reports") },
handler: async (ctx, { reportId }) => {
const user = await currentUser(ctx, {})
const fullReport = await ctx.db.get(reportId)
if (!fullReport) return null
const { cost, recordingUrl, ...report } = fullReport
if (user?.admin) {
if (fullReport.userId) {
const reportUser = await ctx.db.get(fullReport.userId)
const fullReportWithUser = { ...fullReport, reportUser }
return fullReportWithUser
} else {
return fullReport
}
} else {
return report
}
},
})
Thanks
35 Replies
Michal Srb
Michal Srb•8mo ago
I don't see anything wrong with the code. What does the type look line now, and what do you expect? What is the type of fullReport and report in the return statements?
Prince
PrinceOP•8mo ago
strange that reportUser is not showing as a type in the returned const
No description
Prince
PrinceOP•8mo ago
fullReport and report types are correct within the query code but the types don't reflect in the useQuery frontend retunr
Michal Srb
Michal Srb•8mo ago
Can you hover over and screenshot: fullReport , fullReportWithUser and report in the return statements report on the LHS of the useQuery call
Prince
PrinceOP•8mo ago
fullReportWithUser
No description
Prince
PrinceOP•8mo ago
fullReport
No description
Prince
PrinceOP•8mo ago
report
No description
Prince
PrinceOP•8mo ago
report from useQuery on frontend
No description
Michal Srb
Michal Srb•8mo ago
Basic things to try in order: 1. Save the query file 2. make sure your convex dev is successful 3. restart TS server in VS Code
Prince
PrinceOP•8mo ago
yep tried these already, convex dev is running and saving functions as expected I resorted to using another query to get admin details it's strange I remember this working fine in the past
Michal Srb
Michal Srb•8mo ago
I can reproduce it at least, something weird is indeed going on. It's a TS bug. You can work around it by using ternaries instead of if statements:
// This function's return type should be
// {p: number} | {p:number, m: string}
// but it is {p: number}
const fun = (cond: boolean) => {
const x = { p: 1 };
if (cond) {
const combined = { ...x, m: "hello" };
return combined;
} else {
// replace with `return "good"` for correct return type
return x;
}
// This ternary works fine
// return cond ? { ...x, m: "hello" } : x;
};
// This function's return type should be
// {p: number} | {p:number, m: string}
// but it is {p: number}
const fun = (cond: boolean) => {
const x = { p: 1 };
if (cond) {
const combined = { ...x, m: "hello" };
return combined;
} else {
// replace with `return "good"` for correct return type
return x;
}
// This ternary works fine
// return cond ? { ...x, m: "hello" } : x;
};
Michal Srb
Michal Srb•8mo ago
GitHub
If statement return type with union inferred incorrectly · Issue #5...
🔎 Search Terms union, spread, return, inferrence 🕗 Version & Regression Information This is the behavior in every version I tried, and I reviewed the FAQ for entries about union, spread return ...
Prince
PrinceOP•8mo ago
thanks, it still doesn't look like it works with layered terneries
export const getReport = query({
args: { reportId: v.id("reports") },
handler: async (ctx, { reportId }) => {
const user = await currentUser(ctx, {})

const fullReport = await ctx.db.get(reportId)
if (!fullReport) return null

const { cost, recordingUrl, ...report } = fullReport
const reportUser = fullReport.userId
? await ctx.db.get(fullReport.userId)
: null

return user?.admin
? fullReport.userId
? { ...fullReport, reportUser }
: fullReport
: report
},
})
export const getReport = query({
args: { reportId: v.id("reports") },
handler: async (ctx, { reportId }) => {
const user = await currentUser(ctx, {})

const fullReport = await ctx.db.get(reportId)
if (!fullReport) return null

const { cost, recordingUrl, ...report } = fullReport
const reportUser = fullReport.userId
? await ctx.db.get(fullReport.userId)
: null

return user?.admin
? fullReport.userId
? { ...fullReport, reportUser }
: fullReport
: report
},
})
same issue with this
Prince
PrinceOP•8mo ago
ts explorer:
No description
Prince
PrinceOP•8mo ago
that _object type definition doesn't carry over
No description
Prince
PrinceOP•8mo ago
not seen this before, very strange
Michal Srb
Michal Srb•8mo ago
That code works for me. Are you on latest stable TS version? (you can tell by clicking the {} button on the bottom of TS)
Prince
PrinceOP•8mo ago
5.4.5 yep
Michal Srb
Michal Srb•8mo ago
What do you see when you hover getReport ?
Michal Srb
Michal Srb•8mo ago
No description
Prince
PrinceOP•8mo ago
No description
Michal Srb
Michal Srb•8mo ago
Can you share the reports schema so I can get a closer repro
Prince
PrinceOP•8mo ago
export default defineSchema({
users: defineTable({
// this is UserJSON from @clerk/backend
clerkUser: v.any(),
admin: v.optional(v.boolean()),
}).index("by_clerk_id", ["clerkUser.id"]),
reports: defineTable({
vapiId: v.optional(v.string()),
userId: v.optional(v.id("users")),
transcript: v.optional(v.any()),
status: v.optional(
v.union(
v.literal("waiting"),
v.literal("processing"),
v.literal("ready"),
v.literal("failed")
)
),
title: v.optional(v.string()),
summary: v.optional(v.array(v.string())),
diagnosis: v.optional(
v.array(
v.object({
name: v.string(),
differentials: v.array(v.string()),
demographics: v.array(v.string()),
symptoms: v.array(v.string()),
indicators: v.array(v.string()),
contraindicators: v.optional(v.array(v.string())),
prognosis: v.string(),
treatment: v.string(),
tests: v.array(v.string()),
})
)
),
referrals: v.optional(
v.object({
referrals: v.array(
v.object({
specialist: v.string(),
recommendation: v.string(),
})
),
tests: v.array(
v.object({
item: v.string(),
description: v.string(),
})
),
})
),
recordingUrl: v.optional(v.string()),
cost: v.optional(v.number()),
duration: v.optional(v.number()),
})
.index("by_user", ["userId"])
.index("by_vapi_id", ["vapiId"]),
})
export default defineSchema({
users: defineTable({
// this is UserJSON from @clerk/backend
clerkUser: v.any(),
admin: v.optional(v.boolean()),
}).index("by_clerk_id", ["clerkUser.id"]),
reports: defineTable({
vapiId: v.optional(v.string()),
userId: v.optional(v.id("users")),
transcript: v.optional(v.any()),
status: v.optional(
v.union(
v.literal("waiting"),
v.literal("processing"),
v.literal("ready"),
v.literal("failed")
)
),
title: v.optional(v.string()),
summary: v.optional(v.array(v.string())),
diagnosis: v.optional(
v.array(
v.object({
name: v.string(),
differentials: v.array(v.string()),
demographics: v.array(v.string()),
symptoms: v.array(v.string()),
indicators: v.array(v.string()),
contraindicators: v.optional(v.array(v.string())),
prognosis: v.string(),
treatment: v.string(),
tests: v.array(v.string()),
})
)
),
referrals: v.optional(
v.object({
referrals: v.array(
v.object({
specialist: v.string(),
recommendation: v.string(),
})
),
tests: v.array(
v.object({
item: v.string(),
description: v.string(),
})
),
})
),
recordingUrl: v.optional(v.string()),
cost: v.optional(v.number()),
duration: v.optional(v.number()),
})
.index("by_user", ["userId"])
.index("by_vapi_id", ["vapiId"]),
})
Michal Srb
Michal Srb•8mo ago
Can you try again with this code? This one works but doesn't match your screenshot with the type of getReport
Prince
PrinceOP•8mo ago
I just broke apart some of the ternary steps to check type defs but sure
Prince
PrinceOP•8mo ago
No description
Prince
PrinceOP•8mo ago
must just be an issue on my end if you cant reproduce
Michal Srb
Michal Srb•8mo ago
Yeah that looks good The second type is hidden in the ...
Prince
PrinceOP•8mo ago
but then on the frontend 😖
No description
Prince
PrinceOP•8mo ago
pain
Michal Srb
Michal Srb•8mo ago
Yeah it doesn't show up in the typeahead but the union is there
No description
Prince
PrinceOP•8mo ago
perfect, thank you
No description
Prince
PrinceOP•8mo ago
would be ideal if it recognised without it being forced into it
Michal Srb
Michal Srb•8mo ago
So it is kinda a hard-to-work-with type even when it works correctly, you might want to use a tagged union or always set reportUser.
return user?.admin
? fullReport.userId
? { ...fullReport, reportUser }
: {...fullReport, reportUser: null}
: {...report, reportUser: null}
return user?.admin
? fullReport.userId
? { ...fullReport, reportUser }
: {...fullReport, reportUser: null}
: {...report, reportUser: null}
Prince
PrinceOP•8mo ago
this is great though thanks Michal great idea

Did you find this page helpful?