I'm trying to create a configuration function for executing set of functions with a predefined context. Here are my types:
type Config<T> = {
context: T;
};
type Hooks<T> = {
hooks: T;
};
type FunctionWithThis<T> = (this: T, ...args) => any;
In my configure function I want to merge context and my target functions, but pass context parameter as this to functions:
const configure = <TContext extends Object,
THooks extends Record<string, FunctionWithThis<TContext>>>
(config: Config<TContext> & Hooks<THooks>) => {
const result = {
get data() { return config.context; }
};
Object.entries(config.hooks).forEach((action) => {
result[action[0]] = (...args) => action[1].call(config.context, ...args);
});
return result as { data: TContext; } & THooks;
};
It can be used like this:
const engine = configure({
context: {
foo: 12
},
hooks: {
log() {
console.log(this.foo); // this.foo is typed correctly here
}
}
});
const data = engine.data;
engine.log(); // error here
VS Code gives me following error in the last line:
The 'this' context of type '{ data: { foo: number; }; } & { log(this: { foo: number; }): void; }' is not assignable to method's 'this' of type '{ foo: number; }'. Property 'foo' is missing in type '{ data: { foo: number; }; } & { log(this: { foo: number; }): void; }' but required in type '{ foo: number; }'.
I believe that it means that I'm trying to call log function in the wrong context.
Is it possible to change context type for each function in the output?
Or maybe there is another way to solve it?
Since
THooksmust have members of type(this: T, ...args: any[]) => any, the functions onTHookswill requirethisto be of typeTContext. And while your function does ensure thethiswill indeed be ofTContext, the types of the retuned functions don't reflect this. So when calling one of these functions onengine, ts looks at the type ofenigineand sees it is not the appropriatethisforlogand issues an error.Since you pass in
thisinside your implementation, you need to erase thethisparameter from the result. You can do this with a mapped conditional type:Playground Link