You can create a type similar to UnionToIntersection. The reason we can't use that directly is that UnionToIntersection<valueByKey['key1' | 'key2']>
will result in UnionToIntersection<'A' | 'B' | 'C'>
which will be never.
type valueByKey = {
key1: 'A' | 'B';
key2: 'B' | 'C';
key3: 'C' | 'D'
}
type ValueIntersectionByKeyUnion<T, TKey extends keyof T> = {
[P in TKey]: (k: T[P])=>void
} [TKey] extends ((k: infer I)=>void) ? I : never
type valueB = ValueIntersectionByKeyUnion<valueByKey, 'key1' | 'key2'>
Playground Link
Understanding UnionToIntersection
will be useful in understanding this type as well, so I suggest you read jcaz's write up there.
What we have to do is create each of the function signature from each property of valueByKey
and then we can apply the conditional type to contravariantly extraction the desired intersection.
For valueByKey
, the first part { [P in TKey]: (k: T[P])=>void }
will give us a type with the same keys but with the types as function sigantures where the original type is not the type of the parameter:
{
key1: (k: "A" | "B") => void;
key2: (k: "B" | "C") => void;
}
We then create a union of these function signatures using [TKey]
, thus resulting in ((k: "A" | "B") => void) | ((k: "B" | "C") => void)
.
We then apply a conditional type to extract the type of the parameter extends ((k: infer I)=>void) ? I : never
. Basically we are asking the compiler what this union of function signatures is callable with, and the answer is an intersection of the parameter types, resulting in the desired intersection.