Józef Podlecki
Józef Podlecki

Reputation: 11283

Discriminate Union based on array literals

I would like to compute an union from the existing union using literals in array.

Is it possible?

Example


interface Intf1 {
    type: "one";
    data: {
        param: boolean;
    };
}

interface Intf2 {
    type: "two";
    data: string;
}

interface Intf3 {
    type: "three";
    data: {
        param: number;
    };
}

const registered = ["one", "two"] as const;

type CombinedIntf = Intf1 | Intf2 | Intf3;

type CustomIntersect = ? 

const getObj = () => {
    return {} as any; // impl 
};

const obj: CustomIntersect<CombinedIntf, typeof registered> = getObj();
// Intf1 | Intf2

switch(obj.type) {
    case "one":
        console.log(obj.data.param);
    break;

    case "two":
        console.log(obj.param);
    break;

    case "three": // should give an error ""three" is not comparable to type "one", "two""
        console.log(obj.param);
    break;
}

Thank you!

Upvotes: 0

Views: 79

Answers (1)

jcalz
jcalz

Reputation: 327884

I think you can achieve what you want this way:

type CustomIntersect<T, R extends readonly PropertyKey[]> =
  Extract<T, { type: R[number] }>

Here I'm using the built-in Extract<T, U> utility type which returns all the elements of a union T that are assignable to U. Since you want the second type argument R to be the type of an array of keys, we will get the union of its element types by looking up it's number-keyed properties, as R[number]. By Extracting all members of the T union that are assignable to {type: R[number]}, we should grab just those members you care about:

const obj: CustomIntersect<CombinedIntf, typeof registered> = getObj();
// const obj: Intf1 | Intf2

switch (obj.type) {
  case "one":
    console.log(obj.data.param);
    break;

  case "two":
    console.log(obj.data.toUpperCase()); // <-- changed this
    break;

  case "three": // error!
    // ~~~~~~~
    // Type '"three"' is not comparable to type '"one" | "two"'.(2678)
    break;
}

Playground link to code

Upvotes: 4

Related Questions