Suppose I have generic function
function example<T>(a: T, b: number): SomeType<T> {
// code goes here
}
But let's say for the sake of argument that I don't know whether example
was generic on the type of the first or second parameter, or maybe it was generic with two type parameters. My goal is to determine the return type of the instantiation of example
that takes argument tuple [string, number]
. (So it would be nice to have something like InstantiatedReturnType<F, ParamTuple>
so that in this case InstantiatedReturnType<typeof example, [string, number]>
would be SomeType<string>
.) I have verified that typeof example
does extend the type (...args: [string, number]) => any
, but I have not been able to find a way to extract the return type of the instantiation that has this parameter tuple type. In particular, if I take the intersection of the function types typeof Example
and (...args: [string, number]) => any
it has ReturnType< >
equal to either any
or unknown
depending on the order I list the two types in the intersection. Also, because function types are contravariant in their parameters, I tried taking ReturnType< >
of the union of these two types, but that didn't help either. Any suggestions or guidance would be welcome.
(My point about not knowing the structure of the template is I don't have the information to be able to say ReturnType<typeof example<string>>
because maybe the instantiation that matches arguments [string, number]
is actually example<number>
because example
was actually generic on the second parameter type, and explicit in the first parameter as a string. The use case is that I am given an object whose values are alternative functions I might want to call, some of which might be generic, and I am selecting the one to call based on matching the type of an argument tuple I have. That part works -- I can successfully extract the key (as a concrete type with just the one string inhabitant) whose value is a (possibly generic) function callable on the tuple type of the arguments I've got, but I also need to express the return type of what the call will produce, and I can't seem to manage that.)
UPDATE: A stripped down example of what I am trying to accomplish is in this playground. It shows selecting the proper key, and one unsuccessful attempt to obtain the return type of the "matching instantiation".
CodePudding user response:
No, this is unfortunately not possible in TypeScript as of TS4.9. There is no way to express operations on generics at the type level; there are no "generic generics" or higher kinded types, as requested in the longstanding open feature request at microsoft/TypeScript#1213.
So, while the compiler can figure out the output type of a generic function given a particular input if you actually call the function at the expression level (if example("abc", 123)
appears in your code), and while there's even some support for higher order generic functions (e.g., you could get call(example, "abc", 123)
to yield string
if call
has type <A extends any[], R>(f: (...args: A)=>R, ...args: A)=>R
), there is no pure type-level equivalent. At some point you have to actually call the functions with the actual inputs (or convince the compiler that you are doing so) to see what the output types will be.
There was a pull request at microsoft/TypeScript#17961 which would have enabled type-level function application, so that, armed with, type Example = typeof example
you could write type RetType = Example(string, number)
. But this was never merged into the language, and was essentially abandoned along with most of microsoft/TypeScript#6606 a general request to be able to query types for all expressions without actually needing to emit these expressions to JavaScript.
There is currently an open feature request at microsoft/TypeScript#40719 for this feature. It's listed as Awaiting More Feedback, meaning they need to hear more community engagement before even considering making it part of the language. If it matters to you, you could give it a