1
enum keyEnum {
    firstKey = 1,
    secKey = 2,
    thirdKey = 3
};

enum firstPropEnum {
    a = 'a',
    b = 'b',
};

enum secPropEnum {
    c = 'c',
    d = 'd',
};

type firstAndSecPropEnum = firstPropEnum | secPropEnum;

type keyPropObj = {
    [keyEnum.firstKey]: { prop: firstPropEnum },
    [keyEnum.secKey]: { prop: secPropEnum },
    [keyEnum.thirdKey]: { prop: firstAndSecPropEnum },
};

type getKeyProp<T extends keyEnum> = keyPropObj[T]['prop'];

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey | keyEnum.secKey> // Result secPropEnum | firstPropEnum
// Expected Result secPropEnum.
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey | keyEnum.firstKey> // Result firstPropEnum | secPropEnum
// Expected Result firstPropEnum.
type getKeyPropResult3 = getKeyProp<keyEnum.secKey | keyEnum.firstKey> // Result firstPropEnum | secPropEnum
// Expected Result never;

So i was expecting to get an intersection rather than a union. The Result should be a value that in common among all the resulting props. Any help on the same would be highly appreciated.

Amol Gupta
  • 2,054
  • 1
  • 14
  • 29

2 Answers2

3

You can convert unions to intersections with TS2.8 and above. In your case I'd probably do something like this:

type UnionToIntersection<U> =
  (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;

// use Lookup<T, K> instead of T[K] in cases where the compiler 
//  cannot verify that K is a key of T
type Lookup<T, K> = K extends keyof T ? T[K] : never;

type getKeyProp<T extends keyEnum> = Lookup<UnionToIntersection<keyPropObj[T]>, 'prop'>;

And the types you want fall out as desired:

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey | keyEnum.secKey> // secPropEnum.
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey | keyEnum.firstKey> // firstPropEnum.
type getKeyPropResult3 = getKeyProp<keyEnum.secKey | keyEnum.firstKey> // never.

Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
0

Well, you could always intersect the types yourself:

type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey> & getKeyProp<keyEnum.secKey>
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey> & getKeyProp<keyEnum.firstKey>
type getKeyPropResult3 = getKeyProp<keyEnum.secKey> & getKeyProp<keyEnum.firstKey>

A rather ugly alternative that works for up to 6 keys:

type getKeyPropSingle<T extends keyEnum> = keyPropObj[T]['prop'];
type getKeyProp<
    T1 extends keyEnum | null = null,
    T2 extends keyEnum | null = null,
    T3 extends keyEnum | null = null,
    T4 extends keyEnum | null = null,
    T5 extends keyEnum | null = null,
    T6 extends keyEnum | null = null,
> = (T1 extends keyEnum ? getKeyPropSingle<T1> : unknown) &
    (T2 extends keyEnum ? getKeyPropSingle<T2> : unknown) &
    (T3 extends keyEnum ? getKeyPropSingle<T3> : unknown) &
    (T4 extends keyEnum ? getKeyPropSingle<T4> : unknown) &
    (T5 extends keyEnum ? getKeyPropSingle<T5> : unknown) &
    (T6 extends keyEnum ? getKeyPropSingle<T6> : unknown);

type second = getKeyProp<keyEnum.secKey>;
type getKeyPropResult1 = getKeyProp<keyEnum.thirdKey, keyEnum.secKey>
type getKeyPropResult2 = getKeyProp<keyEnum.thirdKey, keyEnum.firstKey>
type getKeyPropResult3 = getKeyProp<keyEnum.secKey, keyEnum.firstKey>
H.B.
  • 166,899
  • 29
  • 327
  • 400
  • I was looking for a more scalable approach. Is there a way to iterate over the enum and proceed accordingly. – Amol Gupta Mar 01 '19 at 10:13
  • @AmolGupta: There probably is no clean solution, at least i know of none. Added another option that works around the [lack of variadic generics](https://github.com/Microsoft/TypeScript/issues/5453) to some degree. – H.B. Mar 01 '19 at 12:01
  • @AmolGupta: Interesting that this is actually possible, but to be honest i would neither write such code nor recommend using it. Conditional types in combination with mapped types are hardly readable and difficult to understand. – H.B. Mar 01 '19 at 16:20