-
Notifications
You must be signed in to change notification settings - Fork 161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to achieve better errros for discriminated unions #1017
Comments
Just found these PRs, which seem to be related: |
The kinda core issue here is that I get a very undescriptive error for union types:
|
@AlbertMarashi Hiya, TypeBox doesn't support DiscriminatedUnion because they are not part of the Json Schema specification, however you can implement them manually. In the case of generating errors for them, you can override TypeBox's error generation function and intercept custom structures and create errors for them. The following is an example of how you might approach this. import { SetErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/errors'
import { Type, KindGuard, ValueGuard, TSchema, TUnion } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'
// Guard for DiscriminatedUnion
function IsDiscriminatedUnion(schema: TSchema): schema is TUnion {
return (
// the schema is union with discriminantKey property ...
KindGuard.IsUnion(schema) && ValueGuard.IsString(schema.discriminantKey) &&
// ... and where union only contains object types
schema.anyOf.every(variant =>
// ... where each variant has a literal string property matching the discriminantKey
KindGuard.IsObject(variant) && KindGuard.IsLiteralString(variant.properties[schema.discriminantKey])
)
)
}
// Override the global error function
SetErrorFunction((param) => {
const { schema } = param
return IsDiscriminatedUnion(schema)
? `Expected either ${schema.anyOf.map(schema => schema.properties.type.const).join(', ')}`
: DefaultErrorFunction(param)
})
// Create Union with discriminantKey
const T = Type.Union([
Type.Object({ type: Type.Literal('A'), value: Type.Number() }),
Type.Object({ type: Type.Literal('B'), value: Type.String() }),
Type.Object({ type: Type.Literal('C'), value: Type.Boolean() }),
], {
discriminantKey: 'type'
})
// Test
Value.Parse(T, null) // Error: 'Expected either A, B, C' Additional Information on SetErrorFunction can be found below https://github.com/sinclairzx81/typebox?tab=readme-ov-file#error-function Will close this one out for now Cheers! |
@sinclairzx81 What I meant more so is that, when I know the Right now, I just get something like |
I must say I also have this problem with unions, is it possible to somehow split it and understand/display actual error for one case of union? |
It would be really nice if we could create custom types, that can extend from the built in typebox error handling like how was kinda mentioned in Like a user-defined type like So, in other words, something like: const discriminated_union_kind = symbol("DiscriminatedUnion")
TypeBox.registerType({
kind: discriminated_union_kind
Parse<T extends TObject[]>(
Schema: typeof DiscriminatedUnion<T>,
value: unknown,
DeferParse: ...
): Static<typeof DiscriminatedUnion<T> {
let discriminant = schema.discriminant
if (!value[discriminant]) throw new Error("Discriminant key is missing from value, expected ...")
// inefficient example
const Subtype = Schema.anyOf.find(sub_schema => {
sub_schema.properties[discriminant] == value[discriminant]
})
if(!Subtype) throw new Error("Discriminant key ... does not exist in schema")
// else defer to typebox's internal handling
// where this would throw errors specific to that union's subtype
return DeferParse(Subtype, value)
}
// Would we need to do this for things like Check/Clone/Assert/Convert/Create/Clean/Default?
) |
It could even help solve issues like: Since I could customise the parsing logic to reorder keys and implement something like |
I am aware there is an existing library for discriminated unions, but I am wondering if there is some kind of way to use the existing
Value.Parse
functionality such that it hints to the parser which item to use.Zod had a proposed
switch
type which would effectively allow for the developer to determine switching logic for cases where there might be multiple or complicated discriminants.Any plans to add something like this or be able to implement it using the existing systems?
eg:
The text was updated successfully, but these errors were encountered: