Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array without undefined validation with io-ts

We are using io-ts to validate external data from HTTP services. I have written a codec that takes another codec C and returns Either<t.Errors, C | undefined> but actually never fails (just to satisfy t.Type). This is because from the service we get an array of different types and new ones can be added in the future so we want it to continue working but just ignore those that are undefined. Looks like:

export function tryDecode<C extends io.Mixed>(
  codec: C,
  onLeft?: (validation: io.Validation<unknown>) => void,
) {
  return new io.Type<io.TypeOf<C> | undefined, io.OutputOf<C>, unknown>(
    'tryDecode',
    (input: unknown): input is C | undefined => {
      return codec.is(input) || input === undefined;
    },
    (u, c) => {
      const validation = codec.validate(u, c);

      return fold(
        () => {
          if (onLeft) {
            onLeft(validation);
          }

          return io.success(undefined as C | undefined);
        },
        (right: C | undefined) => io.success(right),
      )(validation);
    },
    io.identity,
  );
}

I am trying to write a codec arrayWithoutUndefined that works just like io.array but removes any element that is undefined. It should not fail (unless it's not an array). My attempt so far:

export function arrayWithoutUndefined<C extends io.Mixed>(
  codec: C,
  name = `arrayWithoutUndefined<${codec.name}>`,
) {
  const arr = io.array(codec);

  return new io.Type(
    name,
    (u): u is Array<io.TypeOf<C>> => arr.is(u) && !u.includes(undefined),
    (u, c) =>
      either.chain(codec.validate(u, c), as =>
        io.success(as.filter(notUndefined)),
      ),
    arr.encode,
  );
}

The notUndefined is a helper that ensures (a: A | undefined): a is A => .... Then I would use them together as const Modules = arrayWithoutUndefined(tryDecode(UnionOfDifferentModules)) but Modules is still expressed as Array<UnionOfDifferentModules | undefined> when I want it to just be Array<UnionOfDifferentModules>. Is this possible to express with TypeScript/io-ts somehow? I looked into conditional types but unfortunately have no idea how to express.

like image 516
Filuren Avatar asked Dec 28 '25 16:12

Filuren


1 Answers

This problem may have solved itself in newer versions of typescript that support type guards in array filter statements.

It's also possible that explicit generics for the t.Type would help here.

type Subtract<A, B> = A extends B ? never : A;

return io.Type<Subtract<t.TypeOf<C>, undefined>, unknown[], unknown>(...);
like image 183
Souperman Avatar answered Dec 30 '25 17:12

Souperman



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!