I would like to define a type like:
declare type TheOneMillionDollarType<
Entry extends [string, any],
TrMap extends [Entry[1], any]
> = /* TODO */unknown;
Such that here:
type Input = [ "foo", string ] | [ "bar", number ];
type TrMap = [ string, "string !" ] | [ number, "number !" ];
type Expected = [ "foo", "string !" ] | [ "bar", "number !" ];
type Got = TheOneMillionDollarType<Input, TrMap>;
Expected
and Got
would be the same type.
Very concretely. I would like the code of this playground to compile.
CodePudding user response:
One approach is to define it like this:
type TheOneMillionDollarType<
E extends [string, any],
M extends [E[1], any]
> = E extends unknown ? [E[0], Extract<M, [E[1], any]>[1]] : never;
The type E extends unknown ? ... : never
looks like a no-op, but it's actually a distributive conditional type that splits E
into its union members, evaluates the ...
part for each one, and then unions (unites? unionizes? unifies? whatever) the results back together at the end.
That type is [E[0], Extract<M, [E[1], any]>[1]]
, a tuple type like you want where the first element is the same as the first element of E
. The second element uses the Extract<T, U>
utility type to filter the M
union and find any member(s) assignable to [E[1], any]
. That means we are selecting any member from M
where the first element is assignable to the second element of E
. Once we get that, we then index into it ([1]
) to get its second element.
And that works for your example at least:
type Got = TheOneMillionDollarType<Input, TrMap>;
// type Got = ["foo", "string !"] | ["bar", "number !"]
Note that there are likely a lot of edge cases:
type Hmm = TheOneMillionDollarType<["foo", string | number], [string, "a"]>
// type Hmm = ["foo", "a"]
type AlsoHmm = TheOneMillionDollarType<["foo", number], [0, "z"] | [1, "y"]>
// type AlsoHmm = ["foo", "z" | "y"]
type ThirdHmm = TheOneMillionDollarType<["foo", string] | ["bar", number], [string | number, "a"]>;
// type ThirdHmm = ["foo", never] | ["bar", never]
Bet that third one isn't what you'd want, but maybe you don't care about that edge case. Anyway I recommend you test thoroughly, in case it turns out to be TheThreeDollarFiftyCentType
for your actual use cases.