Usually I use Typescript's Parameters
to receive all parameters required by a function of a class:
get = (...params: Parameters<Controller["get"]>) => {}
It works just fine with predefined types in function signature like this: get(id: number)
.
I'm currently trying to get parameters of function that has the following signature:
functionName<T>(callback: () => Promise<T>)
But with the Parameters
it supposes that the T
is never
and I can't pass my own T
into the parameters of such arrow function to get something like Parameters<Controller["get"]<T>>
to get proper generic when I call my function like in the beginning.
Edit 1: Minimal Reproducible Example
class Test {
genericFunction<T>(obj: T): T {return obj;}
genericFunctionPromise<T>(c: () => Promise<T>): Promise<T> {return c();}
example(params: Parameters<Test["genericFunction"]>) {};
examplePromise(params: Parameters<Test["genericFunctionPromise"]>) {params};
}
CodePudding user response:
TypeScript 4.7 introduced instantiation expressions as implemented in microsoft/TypeScript#47606, so if you have a value f
of a generic function type, you can specify the type parameters of that function without calling it. Let's say your function declaration is
declare function f<T, U>(x: T, y: U): [T, U];
Then you can instantiate T
and U
with, say, number
and string
, to get a new expression:
const g = f<number, string>; // instantiation expression
// const g: (x: number, y: string) => [number, string]
You can even use the typeof
type operator to get the type of such a expression without actually creating one:
type G = typeof f<number, string>;
// type G = (x: number, y: string) => [number, string]
Unfortunately, you can't do this purely at the type level (see this comment on ms/TS#47606). You need that variable f
, or a property of a variable. If you only have the type of f
, then you can't instantiate its type parameters
type F = typeof f;
// type F = <T, U>(x: T, y: U) => [T, U]
type Nope = F<number, string>; // error, F is not generic!
Luckily, inside of Test
, you do have a value of the generic function type you care about. It's this.genericFunction
or this.genericFunctionPromise
:
class Test {
genericFunction<T>(obj: T): T { return obj; }
genericFunctionPromise<T>(c: () => Promise<T>): Promise<T> { return c(); }
example<T>(
...params: Parameters<typeof this.genericFunction<T>>) {
// error ------------------> ^^^^
}
examplePromise<T>(
...params: Parameters<typeof this.genericFunctionPromise<T>>
// error ------------------> ^^^^
) { params }
}
new Test().examplePromise(() => Promise.resolve(100));
// (method) Test.examplePromise<number>(c: () => Promise<number>): void
And that actually works... except that there are errors on this
in TypeScript 4.7. Ugh. This turns out to be a bug in TypeScript, see microsoft/TypeScript#49451. This bug has been fixed at microsoft/TypeScript#49521, so typescript@next
nightly build will work, and it will be released with TypeScript 4.8.
If you need to get this working in TS4.7, you could declare a fake variable of type Test
and use it instead, since we just care about types:
declare const __test: Test; // fake
class Test {
genericFunction<T>(obj: T): T { return obj; }
genericFunctionPromise<T>(c: () => Promise<T>): Promise<T> { return c(); }
example<T>(
...params: Parameters<typeof __test.genericFunction<T>>) {
}
examplePromise<T>(
...params: Parameters<typeof __test.genericFunctionPromise<T>>) {
params
}
}