Given a structure where each property is of type These<E, A>
where E
and A
are different for each prop.
declare const someStruct: {
a1: TH.These<E1, A1>;
a2: TH.These<E2, A2>;
a3: TH.These<E3, A3>;
}
I'm treating These
like this
- left: critical error, computation failed
- right: successful computation
- both: minor error/warning, continue computation
Now I'm looking for a way to combine results of a struct like the one above
declare function merge(struct: Record<string, TH.These<unknown, unknown>>): E.Either<CriticalErrorLeftOnly, {
warnings: unknown[]; // these would be the E of Both
value: Record<string, unknown>;
}>
With an Either I could do sequenceS(E.Apply)(someStruct)
. But this would not work here, as it would also return a left for a both.
EDIT: These<E, A> comes from fp-ts and describes a value that can be E, A or both: https://gcanti.github.io/fp-ts/modules/These.ts.html
SECOND EDIT: This is a POC of what I'm trying to achieve.. basically getting all rights and both values of a struct, while aggregating the lefts. However it is not quite there, as the result also contains the lefty properties with type never
import * as ROA from 'fp-ts/ReadonlyArray';
import * as TH from 'fp-ts/These';
import * as E from 'fp-ts/Either';
type PropertyKey = string | number | symbol;
type AnyRightThese = TH.These<any, any>;
type PropertyError<Properties> = { key: keyof Properties; error: Core.Error.Type };
type UnwrapRight<T> = T extends E.Right<infer A> ? A : never;
export const collect = <Properties extends Record<PropertyKey, AnyRightThese>>(
properties: Properties,
): TH.These<
PropertyError<Properties>[],
{
[K in keyof Properties]: UnwrapRight<Properties[K]>;
}
> => {
const errorsAndWarnings: PropertyError<Properties>[] = [];
const rights: any = {};
let isBoth = true;
for (const k in properties) {
const de = properties[k];
if (TH.isLeft(de)) {
isBoth = false;
errorsAndWarnings.push({ key: k, error: de.left });
} else if (TH.isRight(de)) {
rights[k] = de.right;
} else {
errorsAndWarnings.push({ key: k, error: de.left });
rights[k] = de.right;
}
}
return ROA.isNonEmpty(errorsAndWarnings)
? isBoth
? TH.both(errorsAndWarnings, rights)
: TH.left(errorsAndWarnings)
: TH.right(rights);
};
// example
const struct = {
a: TH.right(1),
b: TH.left('foo'),
c: TH.both(10, 'foobar'),
};
const a = collect(struct);
if (TH.isRight(a)) {
a.right.b; // b should not be part of a as it is of type never
}