sinclairzx81 / typebox

Json Schema Type Builder with Static Type Resolution for TypeScript
Other
4.65k stars 150 forks source link

Support for composite custom kinds #858

Open thes01 opened 3 months ago

thes01 commented 3 months ago

Hello, first of all, thank you for creating this package with such an extensive API!

However, I'm currently struggling with a specific case. I'd like to create a custom typebox Kind (let's say Map or generally any composite structure) that recursively checks the validity of its children (similarly to e.g. T.Record or T.Array).

I can see that in the source code, the FromKind method only yields at most one error (for the object as a whole).

function* FromKind(schema: TSchema, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
  const check = TypeRegistry.Get(schema[Kind])!
  if (!check(schema, value)) yield Create(ValueErrorType.Kind, schema, path, value)
}

Would you consider extending it with a custom iterator? Maybe something like:

...
yield* GetCompositeErrorIterator(ValueErrorType.Kind, schema, path, value)

and adding a SetCompositeErrorIterator, similar as with SetErrorFunction? This iterator would then call the Visit function (which would need to be exported from errors/errors.ts) on all the children.

Adding this would enable simple creation of custom composite kinds that validate their inner data while fully embracing the typebox's error mechanism.

Thank you!

sinclairzx81 commented 2 months ago

@thes01 Hi, thanks for the suggestion!

I really like this idea so will mark for consideration and review. Unfortunately I don't anticipate I'll be able to make updates to the current error pipeline in the short to medium term (as there is some design work going on in this area which may change the way validation and error generation is handled going forward). But I do like the idea of being able to yield multiple errors if required for custom kind cases, ideally under a revised SetErrorFunction.

I'm imagining something like the following where the SetErrorFunction is updated to accept a generator function, and where custom kinds can be intercepted and yielded.

SetErrorFunction(function * (params) {
   // intercept custom error
   if(params.schema[Kind] === 'Foo') {
      yield 'error1'  // yield custom error messages.
      yield 'error2'
      yield 'error3'
   }
   // defer to default generation
   yield * DefaultErrorFunction(params)
})

I'll leave this issue open for now (there might be a way to get this in under the current design, possibly on the next minor revision), but may move to into discussions. As mentioned, there's actually some architectural work going on atm to revise the Value, Error and Compiler submodule infrastructure (which is likely to take months to complete), but would like to discuss frontend API designs once the new architecture is a bit more concrete.

Cheers! S