简体   繁体   中英

Get union codec from type values in io-ts

I am trying to create union codec from type codec in io-ts. What I am trying to achieve is basically transforming from:

import * as t from 'io-ts'

const FilterTypeC = t.type({
        POTATO: t.literal('POTATO'),
        CABBAGE: t.literal('CABBAGE'),
        BANANA: t.literal('BANANA'),
        TOMATO: t.literal('TOMATO'),
    });

into:

const FilterTypeUnionC = t.union([t.literal('POTATO'), t.literal('CABBAGE'), t.literal('BANANA'), t.literal('TOMATO')])

Is there some kind way for that in io-ts? I tried adapating similar typescript example but without success.

If I understand correctly what you're looking for, you have a TypeC codec already, and are trying to create a new codec that checks for the union of the possible values of that base codec? The TypeScript type you're looking for would be something like this?

type valuesOfCodec<T> = t.Type<T[keyof T>, T[keyof T], unknown>;

where the codec would decode the values of the given interface.

I don't think that io-ts supports this out of the box, but I was able to write a quick function to create a codec given a TypeC .


// This helper is needed because `t.union` expects at least two codecs
function hasAtLeastTwoItems<T>(t: T[]): t is [T, T, ...T[]] {
  return t.length > 1;
}

// This is the main helper which pulls the value codecs out of a t.TypeC
function valuesOf<T extends t.Props>(
  type: t.TypeC<T>
): t.Type<t.TypeOfProps<T>[keyof T], t.TypeOfProps<T>[keyof T], unknown> {
  const valueCodecs: t.Mixed[] = [];

  for (const key of Object.keys(type.props)) {
    // Grab all of the value codecs out of the iterable properties of the
    // input type's `props` field.
    valueCodecs.push(type.props[key]);
  }

  // If the original type has at least two fields, we can make a union
  // out of the values.
  if (hasAtLeastTwoItems(valueCodecs)) {
    return t.union(valueCodecs);
  }
  // If the type has one field, then the value codec will just be that
  // fields codec.
  if (isNonEmpty(valueCodecs)) {
    return valueCodecs[0];
  }

  // If the type has no fields, then we shouldn't really be decoding
  // successfully at all so I just threw together this `t.Type` that
  // never succeeds at decoding.
  return new t.Type<unknown, unknown, unknown>(
    "always fail",
    (x): x is unknown => false,
    (i, c) => t.failure(i, c, "Cannot decode this codec"),
    (i) => i
  );
}

const FilterUnionTypeC = valuesOf(FilterTypeC);
console.log(FilterUnionTypeC.decode("POTATO")); // -> Right<...>

This should do the trick, but I would warn slightly that this is relying on special meta data present on the t.TypeC class so this won't work with other codecs even if the A type is a record / interface.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM