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.