Intersection/Union Validation Errors
I'm a bit confused around how errors are calculated when validating objects using types involving intersections/unions.
For example, given the following
I would expect
res
to contain errors on both name
and a
since they are the common required fields, but instead I only get an error reported on a
. Is this because ArkType (or TS?) needs to have a fully resolved(?) type before it can validate all fields?7 Replies
Giving good errors for unions is always tricky. Generally, you want to only include one error per branch, otherwise it is just too much information to usefully consume (and is also inefficient checking branches that have already been eliminated).
My guess about why these errors might not match your intuitions is this is a discriminated union because
a
contains two disjoint values. To check the union in constant time, and generally give more helpful errors, we first check what the value of a
is to know what other properties to look for. If a
is not present, we know the data is invalid on every branch and fail fast.
If you're interested, you can check out some of the unit tests here. There are quite a lot of cases where we go out of the way to try and simplify and clarify union errors:
https://github.com/arktypeio/arktype/blob/4fe96236055daa82632bbfd48945b5ea0ab88491/ark/type/__tests__/traverse.test.ts
However, if some case feels particularly bad or actually incorrect, feel free to log an issueAh, that makes sense, thanks for clarifying.
Slightly related: this came about because of errors I was seeing while using the
react-hook-form
resolver for ArkType - the error from rhf
wasn't specifying any key at all for errors in a (sort of) complex union/intersection type which was concerning. I noticed the resolver is using an outdated version of ArkType - do you think the lack of keys in the error feedback is because of the outdated library, or is there a chance the type is too complex to work out what's going wrong?
I appreciate there's a lot of unknowns in there, just thought I would raise it/ask 😄I think rhf specifies AT as a peer dependency so it shouldn't matter as the integration is very superficial.
Generally I would be very careful about large/complex unions w/ forms.
You'll probably get better results with a hybrid approach where instead you specify the shape of the form as a single object with optional properties for keys that are not required on all branches, then use
.narrow
to make additional comparisons and add errors yourself
Honestly avoiding those kind of unions is actually good practice in every context both for runtime validation and within TS itselfYeah I think using a single object would make life a lot easier in this scenario.
Honestly avoiding those kind of unions is actually good practice in every context both for runtime validation and within TS itselfIs this for performance or maintenance/our own sanity reasons?
Clarity, performance and error message readability all suffer when unions start to get too complex
At least in ArkType everything is fully reduced and normalized, but in TS you can get stuff like
(props) & ({a: true} | (props2 & ({b: true} | {c: true}) )
and it is basically impossible to tell what the type is doing from looking at itYeah very true. The in-editor experience can be great when the branching kicks in, but I suppose it's all about balance.
Thanks David!
It can, but it can also be terrible 😅 Sort of a case-by-case basis thing, but basically as unions become more complex, it's less likely you'll get good errors or autocomplete