Skip to content

Custom errors #71

@johannes-lindgren

Description

@johannes-lindgren

#70 introduces a path in the error message which allows you to see where the parsing error occurred.

{
  tag: 'failure'
  error: 'not a number',
  path: [{
    tag: 'object',
    key: 'a',
  },{
    tag: 'array',
    index: 0,
  }],
}

Which means that data.a[0] failed to parse.

However, if the user adds their own parsers, they might also want to add their own custom errors that adhere to a different format. For example, they might want to include a different type of path segment that points to an index within a string:

{
  tag: 'string',
  index: number
}

However, as it stands, they're limited to tags object and array. According to this proposal, the path and error properties should be removed from the library and replaced by a new property error:

type ParseFailure<Error> = {
  tag: 'failure'
  error: Error
}

And:

type ParseResult<Value, Error> = ParseSuccess<Value> | ParseFailure<Error>

(The type parameter should be optional, so that you can write just Parse<?>.)

The higher order parsers should infer the types of the error messages, just as they infer the type parameter Value. So you could end up with a nested error that describes all possible failure outcomes; for example, for parsing an object with two properties a: number and b: string:

{
  tag: 'failure'
  error: {
    tag: 'object'
    errors: {
      tag: 'not-an-object',
    } | {
      tag: 'required-property-missing'
      key: 'a'
    } | {
      tag: 'property-failed'
      key: 'a',
      error: {
        tag: 'parseNumber-failure`
      }
    } | {
      tag: 'required-property-missing'
      key: 'b'
    } | {
      tag: 'property-failed'
      key: 'b',
      error: {
        tag: 'parseString-failure`
      }
    }
  }
}

Or maybe something more easy on the eye?

{
  tag: 'failure'
  error: {
    tag: 'not-an-object'
  } | {
    tag: 'property-failure'
    schema: {
      a: {
        tag: 'property-missing'
      } | {
        tag: 'parseNumber-failure'
      }
      b: {
        tag: 'property-missing'
      } | {
        tag: 'parseString-failure'
      }
    }
  }
}

We could provide function for converting this to a JSON path.

This would work when inferring types, but for explicit type declarations, this would become extremely unergonomic: since TypeScript does not implement partial type inference, the user would need to also specify the second type argument

Cons:

  • It will make the API more complicated, thus making it more complicated to extend; especially for higher order parsers. As a compromise, we could provide a default value as the second type parameter GeneralError which allows the user to bypass the complex error handling logic.
  • The size of the error grows with the size of the object.
  • Lots of work (but doable), but I don't know if anyone would use this feature.

Questions:

  • How are recursive types handled?
  • Will anyone ever be interested in the improved errors?

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedproposalA feature request that hasn't been accepted

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions