I would like to infer the return type of a generic function, but it seems like TypeScript doesn't have anything to help with it.
Given the following piece of code:
// Generic type for object key access
export type Indexable<T = unknown> = {[key:string]:T}
// My function that does something (usually retrieve data from an API and cast to T)
const myFOO = <T extends Indexable = Indexable>(input:Indexable)=>{
return input as T // I know this is pointless but its for simplicity sake
}
// My objects type
type MyType = { a: string }
// Invoke the function with some data and MyType
const foo = myFOO<MyType>({'a':'b'})
/*
Return type is: MyType
This is correct as it should be
*/
type MyInferredReturnType=ReturnType<typeof myFOO>
/*
MyInferredReturnType is:
{
[key: string]: unknown;
}
This is incorrect as I have no way to provide a generic to that function without invoking it
*/
So then I tried to create an interface that matches the function and use that for getting the return type:
interface myBAR<T extends Indexable = Indexable>{
(input:Indexable):T
}
const myBAR: myBAR = <T extends Indexable = Indexable>(input: Indexable) => {
return input as T // I know this is pointless but its for simplicity sake
}
const bar = myBAR<MyType>({ 'a': 'b' }) //Expected 0 type arguments, but got 1.
type MyInferredBARReturnType = ReturnType<myBAR<MyType>>
/*
MyInferredBARReturnType is:
{
[key: string]: unknown;
}
This is incorrect as I my provided type was not passed to the function
*/
Then I tried to add the generic in the interface to the function which fixes the Expected 0 type arguments, but got 1 error but ReturnType<myBAR<MyType>> still returns the wrong type
UPDATE:
There are two possible solutions to this issue but neither are perfect:
- Create an interface with the same name but do not use the interface for the function:
_
// Generic type for object key access
export type Indexable<T = unknown> = { [key: string]: T }
// My objects type
type MyType = { a: string }
interface myFOO<T extends Indexable = Indexable> {
(input: Indexable): T
}
const myFOO = <T extends Indexable = Indexable>(input: Indexable) => {
return input as T // I know this is pointless but its for simplicity sake
}
type MyInferredReturnType = ReturnType<myFOO<MyType>>
//MyInferredReturnType is MyType
const bar = myFOO<MyType>({ 'a': 'b' }) //Expected 0 type arguments, but got
type t = typeof bar
// t is MyType
My issue with this one is the lack of connection between the function and the interface.
- Use Nadia Chibrikova's idea from the comments:
_
const myBAR: myFOO<MyType> = myFOO
type MyInferredBARReturnType = ReturnType<typeof myBAR>
//MyInferredBARReturnType is MyType
My issue with this one is I need to alias the function every time I want to change the generic type.
Is there a better way to achieve?