I don't think this way is idiomatic for typescript:
function foo<T extends <X>(...args: any) => any>(fn: T) {
return function bar<B>(): ReturnType<typeof fn> /***/ {
return fn<B>({ /*config*/ });
}
}
X generic parameter is unused. It should be removed.
Consider this example:
const fn = <T,>(): T => [1, 2, 3] as any as T
const result_0 = fn<number[]>() // number[]
const result_1 = fn<number[] & { tag: 42 }>() // number[]
result_1.tag // 42 but undefined in runtime
This is very unsafe to use generic type which is not related to any argument. I would even say that it is a bad practice in TypeScript. Also you should never use explicit generic arguments during function call< like here x1<{ a: 'A' }>(). Generic { a: 'A' } is not related to any argumenty.
However, there is a amsll expection. You should use explicit generic if you are passing empty array to a function like here:
const array = <T,>(list: T[]) => list
array([]) // never
array<number>([]) // number
AFAIK, this is only one case I know where you are forced to provide explicit generic. Maybe there are other exceptions but I don't know about them. If you are interested in function argument inference you can check my article.
To summarize, this line x1<{ b: 'B' }>() is unsafe as well as using as T in <T>() => Promise.resolve(({/*input*/ } as T)).
ALso, if you want to express type of any function, you should use this annotation (...args: any[]) => any instead of this (...args: any) => any because ...args is an array.
Let's go back to the original example. In order to make it safer, I would write it as follow:
type Fn = (...args: any[]) => any
function foo<Callback extends Fn>(fn: Callback) {
return function bar<Config>(config: Config): ReturnType<typeof fn> /***/ {
return fn(config);
}
}
We can do even better. Since you want to infer only return type, we can use appropriate generic parameter:
type Fn<Return> = (...args: any[]) => Return
const foo = <Return,>(fn: Fn<Return>) => <Config,>(config: Config) => fn(config);
// data argument should have explicit type annotation
const f1 = (data: { a: 'A' }) => Promise.resolve((data));
const x1 = foo(f1)
x1(42).then(elem => {
elem.a // "A"
})
As you might have noticed, it is possible to reduce function annotation. Also, I have not used explicit ReturnType<typeof fn> because typescript is able to infer it.