You're trying to use inference from mapped types in the type signature of mergeEithers
. I find that the compiler isn't always able to extract the desired amount of information in such cases. You are passing in a value eithers
of type { [K in keyof Ds]: Either<E, Ds[K]> }
and hoping the compiler can infer both E
and Ds
from it. Generally speaking it will be able to do something reasonable with Ds
(since it's a homomorphic mapping) and probably give up before you get anything useful for E
.
In cases where this sort of "backwards" inference isn't reliable, I usually go for the brute-force "forwards" inference: if I want the compiler to infer a type A
, I should pass in a value of type A
. To that end, we will have eithers
be of type A
, which will tend to be inferred properly. Then we will use A
to compute the desired output type which used to be called Either<E, Ds>
:
declare function mergeEithers<A extends GenericTuple<Either<any, any>>>(
eithers: A
): Either<
Extract<A[number], ["error", any]>[1],
{ [K in keyof A]: Extract<A[K], ["data", any]>[1] }
>;
Here I'm using the Extract
utility type to pull out the error and the data parts of the union. The type Extract<A[number], ["error", any]>[1]
should be the union of all the possibly-different error types of the eithers
tuple, while the type Extract<A[K], ["data", any]>[1]
should be the data type for each element K
of the eithers
tuple.
Let's see if it works:
const unpacked = mergeEithers([either1, either2])
// const unpacked: Either<MessageError, [string, number]>
Looks good.
Playground link to code
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…