2

I am trying to understand how to "un-index".

Given:

type Example = {
    sayHi: {
        name: string;
    } | {
        sayHi: string;
    };
    cure: {
        bool: boolean;
    } | {
        cure: string;
    };
}

How do I get:

({name: string; } | { sayHi: string; }) & ({bool: boolean } | { cure: string })

I've tried but it flattens everything to a union.

type Example2 = Example[keyof Example]
ThomasReggi
  • 55,053
  • 85
  • 237
  • 424

1 Answers1

3

We can use a variation of UnionToIntersection that distributes over the keys of a type an intersects the property values:

type Example = {
    sayHi: {
        name: string;
    } | {
        sayHi: string;
    };
    cure: {
        bool: boolean;
    } | {
        cure: string;
    };
}


type UnionToIntersectionValues<U, K extends keyof U = keyof U> = 
    (K extends unknown ? (k: U[K]) => void : never) extends ((k: infer I) => void) ? I : never

type R = UnionToIntersectionValues<Example>
// type R = ({
//     name: string;
// } & {
//     bool: boolean;
// }) | ({
//     name: string;
// } & {
//     cure: string;
// }) | ({
//     sayHi: string;
// } & {
//     bool: boolean;
// }) | ({
//     sayHi: string;
// } & {
//     cure: string;
// })

Playground Link

Note: Ts will move the intersection inside and the union outside based on the equivalence that (A | B) & (C | D) is (A & C) | (A & D) | (B & C) | (B & D), so the type of R is equivalent to the type you are looking for.

Titian Cernicova-Dragomir
  • 230,986
  • 31
  • 415
  • 357
  • 1
    This is really neat, I was trying to do the iteration over the keys with a recursive type which required union to tuple & was generally a much worse answer. – Gerrit0 Nov 22 '19 at 17:36
  • I'm having an issue with this where it returns unknown if the object is empty. I need to to just send `U` if it's unknown. – ThomasReggi Nov 23 '19 at 02:59
  • @ThomasReggi I think that can be handled in an extra check. something like `keyof U extends never ? "someDefaultType" : UnionToIntersectionValues` – Titian Cernicova-Dragomir Nov 23 '19 at 09:55