I want to use generic type parameter in its own constraint. It is possible at all in typescript?
My code is here:
type Config<T> = {
context: T;
};
type Hooks<T> = {
hooks: T;
};
type FunctionWithThis<T> = (this: T, ...args: any[]) => any;
type RemoveThis<T extends Record<string, FunctionWithThis<any>>> = {
[P in keyof T]: T[P] extends (...a: infer A) => infer R ? (...a:A) => R: never
}
const configure = <TContext extends Object,
THooks extends Record<string, FunctionWithThis<TContext & THooks>>> // problem here
(config: Config<TContext> & Hooks<THooks>) => {
const result = {
get data() { return config.context; }
};
Object.entries(config.hooks).forEach((action) => {
(result as any)[action[0]] = (...args: any[]) => action[1].call(config.context as any, ...args);
});
return result as { data: TContext; } & RemoveThis<THooks>;
};
const engine = configure({
context: {
foo: 12
},
hooks: {
log() {
console.log(this.foo); // this.foo is typed correctly here but I don't have access to another hooks
},
test(str: string) {
}
}
});
Or in the typescript playground
I'm trying to create a configuration function for executing set of functions with a predefined context.
I've managed to create a simple demo version but now I would like to have an ability to call my hooks from another hook. E.g. I want to configure my test
hook to call log
hook.
In order to achieve this I'm trying to pass a union type as a generic parameter to the 'FunctionWithThis' type:
FunctionWithThis<TContext & THooks>
But unfortunately it doesn't give me what I want: I still have intellisense for context by my hooks are unavailable. It seems like generic parameter is resolved to unknown
when it's used as a constraint for itself.
Is it a way to overcome this?
Actually I have even more complex plans: I want to add one more generic parameter to configure
function and for callbacks and also would like to have an ability to call callbacks from hooks and vise versa. So it would look like this: THooks extends Record<string, FunctionWithThis<TContext & THooks & TCallbacks>>>
where TCallbacks
is a new generic parameter which follows THooks
Your specific problem is hard to solve with just generics. Luckily, TypeScript has a special magical type function called
ThisType
as implemented in microsoft/TypeScript#14141 which allows thethis
context of object literal methods to be specified.I'd expect your code to be typed like this:
This works the way you want, I think:
I haven't looked into how to get your more complex plans implemented, but I wanted to make sure you had a way forward on the code in the question as asked.
Playground link to code