Skip to content
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

Validate Enum at a low level #867

Closed
Meriyemelhajoui opened this issue May 10, 2024 Discussed in #370 · 4 comments
Closed

Validate Enum at a low level #867

Meriyemelhajoui opened this issue May 10, 2024 Discussed in #370 · 4 comments

Comments

@Meriyemelhajoui
Copy link

Meriyemelhajoui commented May 10, 2024

Discussed in #370

Originally posted by m4rvr April 5, 2023
Hey! I'm currenlty using Typebox to perform Json validation , and I m using Enums to define my Typebox schema , and When I tried the validation I've remarked that the Typechecker cannot catch the validationError at a low level , that's mean it generate the errorValue at a highlevel , for example let's say I have this schema :

export const ENUM_EXAMPLE=Type.Object(
   {
      "Description": Type.Union([ Type.Number(), Type.String() ]),
      "displayName": Type.Union([ Type.String(), ENUM_DISPLAY_NAME]),
   }
the ENUM_DISPLAY_NAME is a separate object that has his own schema : 
export const ENUM_DISPLAY_NAME=Type.Object(
   {
      "type": Type.Union([ Type.Number(), Type.String() ]),
      "value": Type.Number()
   }

when I give the value inside the ENUM_DISPLAY_NAME a wrong value "wrong input" , the error get catched at a high level :

ENUM_EXAMPLE.displayName : expect an Enum value , instead of giving me ENUM_EXAMPLE.displayName.value : expect a number value .

my question is : Does the TypeChecker can catch errors at a lowest level when we are dealing with Enums ? because when it comes to nested obejcts , it can retrieve the property that trigger the error

@sinclairzx81

@Meriyemelhajoui
Copy link
Author

@sinclairzx81

@sinclairzx81
Copy link
Owner

@Meriyemelhajoui Hi,

TypeBox currently yields only the top most error for Union-like structures (which includes Enum) but doesn't yield any internal errors of that Union. The reason for this is it tries to prevent excessive error generation for values with non-matching sub variants as these tend to produce excessively large (and sometimes duplicated) error results. Consider the following.

const A = Type.Object({ x: Type.Number(), y: Type.Number() })
const B = Type.Object({ x: Type.String(), y: Type.String() })
const C = Type.Object({ x: Type.Boolean(), y: Type.Boolean() })
const U = Type.Union([A, B, C])

// ...

const E = [...Value.Errors(U, { })] // no properties

// Current (top level only)
//
// [
//  { message: 'Expected union value', path: '' }
// ]
//
// -----
//
// Yielded sub variant errors (not supported)
// [
//  { message: 'Required property', path: '/x' },  // For variant A
//  { message: 'Required property', path: '/y' },
//  { message: 'Required property', path: '/z' },
//
//  { message: 'Required property', path: '/x' },  // For variant B (duplicated)
//  { message: 'Required property', path: '/y' },
//  { message: 'Required property', path: '/z' },
//
//  { message: 'Required property', path: '/x' },  // For variant C (duplicated)
//  { message: 'Required property', path: '/y' },
//  { message: 'Required property', path: '/z' },
//
//   ...
// ]

Unfortunately, I don't know of a better solution to this problem that is both performant and meaningful in terms of errors presented. The generation of the top level Union error isn't ideal, but it is the simplest option I've found thus far. I'm open to community thoughts on a better error generation strategy though.

Better Errors with SetErrorFunction

While it's not possible to yield internal errors for Union, you can provide custom error messages which may be a bit more descriptive of the actual error. You can use the SetErrorFunction and DefaultErrorFunction to achieve this, documentation on this can be found at the link below.

https://github.com/sinclairzx81/typebox#error-function

import { SetErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/errors'
import { Type } from '@sinclair/typebox'
import { Value } from '@sinclair/typebox/value'

// Overrides the default error function to intercept schemas with 
// a errorMessage property. This enable you to assign specific
// errors to types and have them generate in error messages.
SetErrorFunction((param) => ('errorMessage' in param.schema) 
  ? param.schema.errorMessage
  : DefaultErrorFunction(param)
)

const Color = Type.Union([
  Type.Literal('Red'),
  Type.Literal('Blue'),
  Type.Literal('Green'),
], {
  errorMessage: "Expected either 'Red', 'Blue' or 'Green'"
})

// ...

const R = [...Value.Errors(Color, 'Orange')]

console.log(R) // [
               //   {
               //     type: 62,
               //     schema: {
               //       errorMessage: "Expected either 'Red', 'Blue' or 'Green'",
               //       anyOf: [Array],
               //     },
               //     path: '',
               //     value: 'Orange',
               //     message: "Expected either 'Red', 'Blue' or 'Green'"
               //   }
               // ]

Hope this provides a bit of insight into the current Union/Enum error generation (as well as a viable workaround with the SetErrorFunction). I do think better error generation may be possible with the introduction of Tagged/Discriminated Unions (which TypeBox doesn't support natively, but may provide facilities for in later revisions). For now though, the above is the best it can provide under the current setup.

Hope this helps
S

@sinclairzx81
Copy link
Owner

@Meriyemelhajoui Hiya,

Might close off this issue as generating union variant errors is generally not supported (at least for now). As per example though, the SetErrorFunction will allow you to generate type specific error messages if you need a bit more detail than just the generic union error TypeBox currently generates.

If you have any questions on the above or follow up suggestions on how to improve the current TypeBox union / enum generated errors, happy to discuss this further on this thread. Will close for now though.

Cheers!
S

@Meriyemelhajoui
Copy link
Author

Thank you for your explanation @sinclairzx81

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants