Type of schema field depend on value of other field
Say we have a schema that on a field called props stored different kinds of objects depending on the value of a field called type. How would we define this schema in convex?
13 Replies
@Michal Srb @sshader 🙏
https://docs.convex.dev/typescript + https://stack.convex.dev/types-cookbook are generally good resources, but to answer the question directly, you probably want something like
v.union(v.object({ kind: v.literal("bird"), props: v.object({ wingspan: v.number() }) }), v.object({ kind: v.literal("dog"), props: ... }))
(a union
of objects, each with a field with a different v.literal
value -- I used kind
vs. type
out of personal preference)
This corresponds to this feature in TypeScript https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#discriminating-unionsTypeScript | Convex Developer Hub
Move faster with end-to-end type safety.
Types and Validators in TypeScript: A Convex Cookbook
It can be tough to wrangle types to behave how you want them to. Thankfully, Convex was designed to make the experience with types perfect. Learn why ...
Handbook - Unions and Intersection Types
How to use unions and intersection types in TypeScript
tagging @Sirian
I've got a bit of a more complex case in my hands and was wondering if I can improve something here:
I want to complete the todos in the code snippet. Any thoughts on whether this is possible? @sshader
I guess I could do a union of multiple filter objects and have different operator types and value types for each, but the goal ofc would be to also constraint the type of the firestoreField referenced in the id
What does the schema for
firestoreFields
look like here?Something like this:
the type field is of essence in this scenario
Not to distract you from the previous question, but is this good practice?
*BlockProperties are standard v.object(), but I can't seem to spread an object validator as done with baseBlockTableProps
re: second question -- yeah that seems fine. We some day want to make it a little easier to grab the fields from a validator.
re: first question -- ok yeah this seems fundamentally hard to do via schema validation since schema validation operates over a single document, and what you want is to validate something in
filters
based off of the document you get if you load fieldId
.
One option would be to have a different table for each data type (e.g. firestoreStringFieldsTable
), but I think you might be better off keeping your schema generic but having a TS wrapper + some helpers. For instance, if you always use the same function to write to filters
, you can load the fieldId
and assert that the types match up instead of relying on schema validation to do this.Sounds good!!
Okay, back with another question:
I have the following table:
when I run this query:
I'd expect the inferred type of
pages
to be inferred as
but instead when I hover over pages I get:
Am I doing something wrong or is this a limitation of Convex? How else should I best assert the type of pages
?This is a limitation of Convex. You can
Infer
the TS type from the validator and then use it to assert the result:
Thanks @Michal Srb 🙂 your joint support has been great!
unless I'm missing something, I'd need something like
as PageWithPageBlocks[]
at the end of the query. Otherwise TS complains due to a type conflictSometimes TS lets you downcast via variable declaration, but when exactly I'm not sure.
as
is the way to go.I just realized that when I go down this path I lose the luxury of accessing system fields, so something like
pages[0]._id
... any ideas?
Specific example where this is an issue:
wondering if perhaps there's a way to hydrate types with system fields...
This feels pretty hacky:
How about casting as this instead?
nifty! didn't know this trick, thanks @RJ 🔥