looping through a v.union of v.literals
We have a huge permission list that is a v.union full of v.literal's. We don't want to duplicate and maintain the list in the db init file if possible. And map/foreach doesn't work on a validator, is there any tricks to this.
29 Replies
does something like this work for you? https://stack.convex.dev/argument-validation-without-repetition
Argument Validation without Repetition
In the first post in this series, the Types and Validators cookbook,, we shared several basic patterns & best practices for reusing types & validators...
I don't think there's an approach that avoids duplication. Colocating might help. The compiler will yell at you if the init value doesn't match the validator, so there shouldn't be much concern for drift between the two.
You might like the helper I made called
literals
which takes …args of string and returns the union validator. So you could have an array of the strings, and spread them into literals
for validation.Yeah I'm interested in that, how do i access it?
npm
convex-helpers
A collection of useful code to complement the official convex package.. Latest version: 0.1.32, last published: 4 hours ago. Start using convex-helpers in your project by running
npm i convex-helpers
. There are no other projects in the npm registry using convex-helpers.Yeah i suppose i could move the insertMany array into the other file for colocating if that is what you mean. We have 3 different permission systems with similar roles but unique permissions, and users can have multiple roles within each system so a simple duplication turns into 3 pairs.
so it should be fine with convex ents right, i haven't been looking as much into the helpers as im not exactly sure what it would be like to blend this all together yet.
Yeah I think most helpers can work with Ents. Ents even rely on customFunction and such to work. The only helpers that come to mind that aren't as easy would be relationship helpers that take in a DatabaseReader / etc. But Ents makes all of that easy without those helpers. If you find anything that doesn't play nicely, let me know!
I cant figure out how to put a array/list or whatever into a literals() other then a manually written list. I may have a knowledge gap but I'm wanting to get the keys from a object and generate a validator of field names and have tried a few different things. So far the only thing that works is:
const arr = ["test", "c121", "c1212"] as const; export const vOptionKeys = literals(...arr);
I have a object export const OptionsObj = {
isVisible: v.boolean(),
... large list,
i want to just do Object.keys(OptionsObj)
and construct a validator. Which doesnt seem possible.
I can't find a way to do this that doesn't involve declaring the keys as an array of literals.
I'm no typescript wizard, so maybe there's a way, but I believe a literal type requires a literal value for static analysis.
If they’re all v.boolean() maybe you could start with the list, then make an object of them from that.
For types of an object you might need to do some type gymnastics
const keys: Array<keyof typeof myObject> = […myObject.keys()]
On my phone so haven’t played around with it
Yeah, you'd have to use a mapped type:
https://www.typescriptlang.org/docs/handbook/2/mapped-types.html
Documentation - Mapped Types
Generating types by re-using an existing type.
Here's a way to do it without
literals
@ampp :
I get this:
oh wait mine is different than yours
ah, my editor is making changes on save, but it doesn't seem to make a difference in the Typescript output
yeah this seems to work:
const o = { a: v.boolean(), b: v.boolean(), c: v.boolean() };
const keys = [...Object.keys(o)] as Array<keyof typeof o>;
const keyLiterals = keys.map(v.literal);
const keyValidators = v.union(keyLiterals[0], keyLiterals[1], ...keyLiterals.slice (2));
the only downside is i have to go out to 20 in the validators union
const keyValidators = v.union(...keyLiterals) would be very elegant if something like that was possible. maybe mapping to strings and using the convex-helpers literals()
chatgpt thinks this should work... const keyValidators = v.union(...keyLiterals.slice(0, 2)); but it doesn't also suggesting just ...keyLiterals, but these days i expect only 30% helpfulness
you shouldn't have to go out to 20, the
...keyLiterals.slice(2)
will do the rest. This is just a shortcoming of v.union
which has two explilcit params to prevent you from doing a union of size one: v.union(v.null())
which is meaningless
The [0], [1], ...[2:] pattern should work for any number@ampp are you saying that last line works with the slice, or it works but you have to put each individual
keyLiterals[n]
into v.union()
?
sounds like the latter, just confirming what you meant when you said it seems to workim doing v.union(
keyLiterals[0], keyLiterals[1], keyLiterals[2], keyLiterals[3], keyLiterals[4],
keyLiterals[5], keyLiterals[6], keyLiterals[7], keyLiterals[8], keyLiterals[9],
keyLiterals[10], keyLiterals[11], keyLiterals[12], keyLiterals[13], keyLiterals[14],
keyLiterals[15], keyLiterals[16], keyLiterals[17], keyLiterals[18], keyLiterals[19]
); right now
okay so spread is not working for you
same for me
@ian I shared what I'm seeing above, curious how this is working for you
I'm not sure what's different for you, but when I do
v.union(keyLiterals[0], keyLiterals[1], ...keyLiterals.slice(2))
it doesn't matter how many object keys there are.The only difference I see is doing
Array<keyof typeof o>
instead of (keyof typeof o)[]
which I suppose has the return type of | undefined
for array access
Weird, not sure why my editor is so certain that Array annotation is superfluous. But I do get the same typescript output either way. I'll double check that I'm not missing something tomorrow, weird that we're getting different results.
oh that does work, its possible i probably tried it like const keyValidators = v.union(keyLiterals[0] ...keyLiterals.slice (1)); because i thought union took a minimum of two arguments, but ... must not count. Although i do remember trying it both ways at various points. and isn't helping that randomly i get typescript delays in the minutes.
Maybe typescript version is at play here too? I'm on 5.4
I just published
convex-helpers@0.1.34-alpha.5
that accepts literals(...keyLiterals)
@ampp @erquhart - can you check that it works for you? If so, I'll publish it in 0.1.35
Didn't actually try to use it as a validator, but Typescript is happy:
I'm using it in several contexts with success so far 🙂
Great! It went out in
0.1.34
after I did some validation tooWorking great! Thanks @ian