I've come across certain challenge with Either.DO
Here's a working example
import * as A from 'fp-ts/Apply'
import * as E from 'fp-ts/Either'
import { pipe } from 'fp-ts/function'
import * as S from 'fp-ts/Semigroup'
import * as string from 'fp-ts/string'
import * as RR from 'fp-ts/ReadonlyRecord';
const parseString = (u: unknown): E.Either<string, string> =>
typeof u === 'string' ? E.right(u) : E.left('not a string')
const parseNumber = (u: unknown): E.Either<string, number> =>
typeof u === 'number' ? E.right(u) : E.left('not a number')
interface Person {
readonly name: string
readonly age: number
}
(() => {
const apS = A.apS(E.getApplicativeValidation(RR.getUnionSemigroup(pipe(string.Semigroup, S.intercalate(', ')))))
const lift = <E, A, B, C extends string>(action: (a: B) => E.Either<E, A>, name: C): (a: B) => E.Either<RR.ReadonlyRecord<string, E>, A> => (a) => pipe(action(a), E.mapLeft((e) => ({ [name]: e })));
const parsePerson = (input: Record<string, unknown>) =>
pipe(
E.Do,
apS('name',lift(parseString, "name" as const)(input.name)),
apS('age', lift(parseNumber, "age" as const)(input.age))
)
console.log(parsePerson({}))
})();
I need the return type of parsePerson(...)
, specifically it's Either.left
to be structurally similar to it's Either.right
So,
the return type of parsePerson(...)
is
const parsePerson: (input: Record<string, unknown>) => E.Either<Readonly<Record<string, string>>, {
readonly name: string;
readonly age: number;
}>
but I need it look like this
const parsePerson: (input: Record<string, unknown>) => E.Either<
readonly name: string;
readonly age: string;
>, {
readonly name: string;
readonly age: number;
}>