Io-ts: Obtain all errors when decoding D.type

Created on 19 Apr 2020  路  7Comments  路  Source: gcanti/io-ts

馃殌 Feature request

Hi I am new to this library and I am currently trying out the new experimental interface. I noticed that the decoders only produce the first error instead of all decoding errors.

Current Behavior

When decoding fails for a field, the the decoder returns immediately with the error of that field.

Desired Behavior

I would like the decoder to continue decoding the remaining fields, collecting all decoding errors. I believe this is the behaviour of the stable implementation. Is there a reason for this change and is there a way to retrieve all errors?

Thank you.

Your environment

| Software | Version(s) |
| ---------- | ---------- |
| io-ts | 2.2.0 |
| fp-ts | 2.5.3 |
| TypeScript | 3.8.3 |

enhancement experimental

Most helpful comment

I don't care about performance if I don't have a problem and when I have a problem I go and optimize locally

@mikearnaldi I agree, the default behavior should be collecting all errors

All 7 comments

Can you provide an example?

@merisbahti this is the current behavior

import * as D from 'io-ts/lib/Decoder'
import { isLeft } from 'fp-ts/lib/Either'
import { draw } from 'io-ts/lib/Tree'

export const Person = D.type({
  name: D.string,
  age: D.number
})

const result = Person.decode({})
if (isLeft(result)) {
  console.log(draw(result.left))
}
/*
required property "name"
鈹斺攢 cannot decode undefined, should be string
*/

this is the desired behavior

/*
required property "name"
鈹斺攢 cannot decode undefined, should be string
required property "age"
鈹斺攢 cannot decode undefined, should be number
*/

Is there a reason for this change

@jakejx mainly for simplicity and performance, though I have not a strong opinion on this. I modeled the error type so we can easily switch to multiple errors.

For the sake of discussion let's turn this around, is there a reason for collecting all decoding errors which counterbalances a possible degraded performance?

My current use case is mainly for API request validations, and I would prefer the API to return all validation errors. I have written a custom type() function to collect all errors as a workaround currently.

I raised the issue because I noticed that the new implementation produced different results, so I wanted to make sure I was using the library correctly.

For reference this is what I (naively) came up with:

export function type<A>(
  properties: { [K in keyof A]: D.Decoder<A[K]> }
): D.Decoder<A> {
  return {
    decode: (u) => {
      const e = D.UnknownRecord.decode(u);
      if (E.isLeft(e)) {
        return e;
      } else {
        const errors = [];
        const r = e.right;
        const a: Partial<A> = {};
        for (const k in properties) {
          const e = properties[k].decode(r[k]);
          if (E.isLeft(e)) {
            const error = Tree.make(k, e.left);
            errors.push(error);
          } else {
            a[k] = e.right;
          }
        }
        const allErrors = fromArray(errors);
        return allErrors._tag == 'Some'
          ? E.left(allErrors.value)
          : D.success(a as A);
      }
    },
  };
}

I think in general there is no one-size-fit-all approach, for a decoding library (especially an FP-oriented one) accumulation of errors is a fundamental feature, on the other end compromising performances in all cases may not be the best choice.

Drawing inspiration from the Validation & Either data-type we can pick the right solution for the specific problem keeping the same encoding but swapping the instance, I would really love to see the same kind of approach here.

My own personal interpretation on performance is pretty simple, I don't care about performance if I don't have a problem and when I have a problem I go and optimize locally so I'd argue by default we should accumulate while leaving the option to short-circuit as a secondary thing.

I don't care about performance if I don't have a problem and when I have a problem I go and optimize locally

@mikearnaldi I agree, the default behavior should be collecting all errors

@jakejx v2.2.1 released

Was this page helpful?
0 / 5 - 0 ratings

Related issues

steida picture steida  路  3Comments

Picoseconds picture Picoseconds  路  3Comments

rjhilgefort picture rjhilgefort  路  6Comments

gabro picture gabro  路  4Comments

spacejack picture spacejack  路  3Comments