Reputation: 11283
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
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 Extract
ing 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;
}
Upvotes: 4