superstruct icon indicating copy to clipboard operation
superstruct copied to clipboard

Typed error

Open kamijin-fanta opened this issue 4 years ago • 7 comments

ref #568

A design proposal for structurally expressing errors. This PR makes it possible to support custom error messages and i18n. This library will be available for displaying errors to users.

For example, size(string(), 1, 100) outputs the error message ${expected} with a length ${of} but received one with a length of ' ${length}'. This change outputs the following error content in addition to the message. It is possible to format the error message according to the context and language.

interface SizeErrorDetail<T extends number | Date> extends ErrorDetail {
  class: 'size'
  actually: T
  min: T | undefined
  max: T | undefined
  minExlusive: boolean
  maxExlusive: boolean
}
  • todos
    • resolve conflicts
    • think about compatibility

kamijin-fanta avatar Feb 01 '21 07:02 kamijin-fanta

@kamijin-fanta this is cool! A few ideas...

  • I think the E generic should maybe represent just a dictionary of values that will be attached to a struct error instead of needing for it to extends Error itself?
  • I think the details should be mixed directly into the error object, instead of living in a details property.
  • We can make the existing Failure.refinement prop part of the error dictionary properties instead, which is nice. And we can do the same for type too.
  • I don't think the SizeErrorDetail should be common across min/max like that, and instead each refinement should be self-contained.
  • Couldn't tell if this already happens, but it would be good to keep the ability to return true/false instead of requiring using a details object, since this makes the library really easy to pick up for people.
  • The InferError utility is a great idea!

Which would make a min(number(), 0) struct look something like:

Struct<number, null, {
  type: 'number'
} | { 
  refinement: 'min'
  min: 0,
  exclusive: false
}>

And Failure might look like:

type Failure<E extends Record<string, any>> = {
  value: any
  key: any
  branch: Array<any>
  path: Array<any>
  message: string
} & E

ianstormtaylor avatar Feb 12 '21 00:02 ianstormtaylor

@ianstormtaylor Thanks for the idea!

The reason I made min / max common is that I feel that I often need it when displaying error messages. For example, "A value of 10 to 20 is allowed. It was actually 25."

I work on a Failure type change.

kamijin-fanta avatar Feb 19 '21 01:02 kamijin-fanta

Do you have any idea for the definition of toFailure, Result and Validator when rewriting to type Failure E extends Record<string, any> ? For example, it is difficult to generate Failure<E> from the following Result type.

export type Result<E extends Record<string, any>> =
  | boolean
  | string
  | Partial<Failure<E>>
  | Iterable<boolean | string | Partial<Failure<E>>>

It may be possible to solve it by redefining the Result type as follows.

export type DescribedResult<E extends Record<string, any>> =
  | boolean
  | string
  | Partial<Failure<E>>
  | Iterable<boolean | string | Partial<Failure<E>>>

export type SimpleResult = boolean | string | Iterable<boolean | string>

export function define<T>(
  name: string,
  validator: SimpleValidator
): Struct<T, null, {}>;

Also, it can be easily implemented by giving up the type definition for detailed error information. Of course I want to avoid this if possible.

kamijin-fanta avatar Feb 19 '21 05:02 kamijin-fanta

@kamijin-fanta good question! I tried messing with it in the TypeScript Playground and came up with this approach which may help? It keeps a "props" dictionary as the third argument which then determines which "results" are allowed to be returned by a validation function.

I wasn't sure how to limit it more than object, but it might work for now?

ianstormtaylor avatar Feb 19 '21 19:02 ianstormtaylor

Hi, what's the status for this PR?

michael-land avatar Apr 08 '21 22:04 michael-land

@xiaoyu-tamu sorry. I couldn't get enough time and left it. I would like to work on it when other work is done, but I would like to welcome other people's contributions.

kamijin-fanta avatar Apr 09 '21 01:04 kamijin-fanta

Maybe the maintainers would consider taking this code in and continuing on it?

haf avatar Jun 13 '22 18:06 haf