This is a follow up from my last post Generic types in classes which has been perfectly answered by @jcalz. Since playing around with this solution, I highly doubt my understanding of Typescript in general. I simply can’t get my head around of what is going on and why certain things “are not working”.
However, I recently wanted to know how I could achieve a typed mapping of taskIds
to their callback
back functions and get a typed result. See here. I have now extended the code to
type Values<T extends string> = {[K in T]: any}
type Results<T extends string, R> = {[K in T]: R}
type Callback<T extends string> = (prev?: any, values?: Values<T>) => any
type Task<
K extends string,
V extends Callback<K>
> = {
taskId: K;
callback: V;
};
class Taskmanager<
S extends string,
const T extends Task<
S,
Callback<S>
>
> {
private prevTaskId?: T["taskId"];
private values!: Values<T["taskId"]>;
private tasks = new Map<T["taskId"], T>
public constructor(tasks: readonly T[]) {
tasks.forEach((task) => {
this.tasks.set(task.taskId, task);
this.values = {...this.values, [task.taskId]: undefined};
});
}
public run<K extends T["taskId"]>(): {[ID in K]: ReturnType<Extract<T, { taskId: ID }>["callback"]>} {
let result = {}
this.tasks.forEach(task => {
const prev = this.prevTaskId ? this.values[this.prevTaskId] : undefined;
const res = task.callback(prev, this.values);
this.values[task.taskId] = res;
this.prevTaskId = task.taskId;
result = {...result, [task.taskId]: res};
})
// Is there a better way than casting the return type?
return result as {[ID in K]: ReturnType<Extract<T, { taskId: ID }>["callback"]>};
}
}
// ---------------
// TEST
// ---------------
const tm = new Taskmanager([
{
taskId: "defineValue",
callback: (prev, values) => {
return 1;
},
},
{
taskId: "usePrev",
callback: (prev) => {
return "hello " + prev;
},
},
{
taskId: "useValue",
callback: (prev, values) => {
// How could I achieve type hints for `values`? Currently values: { [x: string]: any }
// eg. values?.defineValue or values?.useValue
// Since I have return types from the callback functions, could I also infer those? E.g. typeof values?.useValue === 'object' --> true
return {
prev: prev,
usePrevVal: values?.usePrev
}
},
}
]);
const results = tm.run();
// results → { defineValue: number, usePrev: string, useValue: { prev: any, useDefineVal: any } } --> this is perfect!
console.log("Results", results)
// LOG → "Results", { "defineValue": 1, "usePrev": "hello 1", useValue: { "prev": "hello 1", "useDefineVal": 1 }} --> works as intended
Here’s a link to TS Playground
How could I get type hints for the callback values
parameter? Preferably including the return type of the associated callback but having the taskIds
as keys would already be good enough.
Thanks!