got into something that I can't wrap my head around.
I want to have a function as follows in my TypeScript package:
function example<U>(key: keyof U) {
return function<T extends U[typeof key]>(
value: T,
cb: (obj: unknown /* not sure what this should be typed as */) => void
) {
// ...
}
}
And pass beforehand unknown Union into it:
type Union =
| {
color: "red";
status: "error"
}
| {
color: "green";
status: "success"
}
| {
color: "blue";
status: "info"
}
How would I need to type it to get to this desired state?
example<Union>("color")("red", (obj) => {
// obj.color === "red"
// obj.status === "error"
});
Is that even possible?
Thanks.
CodePudding user response:
You can do this although you must either use a wrapper class or another wrapper function because the generic for Union
must be manually passed in and we need another generic to be able to use later on for the keys of the Union
. If we included both generics in the original function then your code would error because you must include all generics explicitly if you are going to include any of them. Here's my solution:
function matcher<T>() {
return function key<K extends keyof T>(key: K) {
return function value<V extends T[K]>(value: V, cb: (data: T & { [_ in K]: V }) => any) {
throw new Error("unimplemented");
};
}
}
type Union =
| {
color: "red";
status: "error"
}
| {
color: "green";
status: "success"
}
| {
color: "blue";
status: "info"
};
matcher<Union>()("color")("red", (obj) => {
type Obj = typeof obj;
// ^? { color: "red"; status: "error"; }
// obj.color === "red"
// obj.status === "error"
});
The reason that I use T & { [_ in K]: V }
for the type of the cb
parameter is because it will get the branch in the union which includes the key with the given value.