gcanti / io-ts

Runtime type system for IO decoding/encoding
https://gcanti.github.io/io-ts/
MIT License
6.68k stars 330 forks source link

Explicit Error Types (Decoder) #636

Closed anthonyjoeseph closed 2 years ago

anthonyjoeseph commented 2 years ago

🚀 Feature request

zod, a runtime validation library, has an intuitive & statically typed dx that exposes possible errors at the type level

import { z } from "zod";

const User = z.object({ username: z.string(), password: z.string() });

const user = User.safeParse({ username: 123, password: 'secret' });

const usernameFailed: boolean = !user.success 
  && !!user.error.format().username
  //                          ^
  //             this is statically typed!

Current Behavior

const User = D.fromStruct({ username: D.string, password: D.string })

const user = decoder.decode({ username: 123, password: 'secret' })

const usernameFailed: boolean = pipe(
  user,
  E.fold(
    (e) => D.draw(e).includes('username'),
    () => false,            //     ^
  )                         //     |
  // have to look at the implementation of 'struct' to know this
) // could overlap with other errors

Desired Behavior

const user: E.Either<
  NonEmptyArray<{
    error: "Expected string";
    path: "username";
    received: Exclude<Primitive, 'string'>;
  } | {
    error: "Expected string";
    path: "password";
    received: Exclude<Primitive, 'string'>;
  }>, 
  string
>

Suggested Solution

A non empty array of a 'sum type' of errors

A bit clunky, but maximizes compile-time information

type DecodeError = { error: string }

type Decoder<I, E extends DecodeError, A> = {
  decode: (i: I) => Either<NonEmptyArray<E>, A>
}

declare const compose: 
  <A, E2 extends DecodeError, B>(to: Decoder<A, E2, B>) => 
    <I, E extends DecodeError>(from: Decoder<I, E, A>) => 
      Decoder<I, E | E2, B>

partial implementation (playground)

Who does this impact? Who is this for?

This approach is useful for displaying error messages (e.g. on forms), suggesting possible solutions, and/or recovering automatically from malformed input

Describe alternatives you've considered

A fork, or even a separate library (io-ts-validation?)

Additional context

Your environment

Software Version(s)
io-ts 2.2
fp-ts
TypeScript
thewilkybarkid commented 2 years ago

Take a look at the proposed error change in https://github.com/gcanti/io-ts/issues/453#issuecomment-842095270.

anthonyjoeseph commented 2 years ago

Woah! I had no idea. Thanks!