简体   繁体   中英

Typescript ReturnType of generic function

The new ReturnType in TypeScript 2.8 is a really useful feature that lets you extract the return type of a particular function.

function foo(e: number): number {
    return e;
}

type fooReturn = ReturnType<typeof foo>; // number

However, I'm having trouble using it in the context of generic functions.

function foo<T>(e: T): T {
    return e;
}

type fooReturn = ReturnType<typeof foo>; // type fooReturn = {}

type fooReturn = ReturnType<typeof foo<number>>; // syntax error

type fooReturn = ReturnType<(typeof foo)<number>>; // syntax error

Is there a way extract the return type that a generic function would have given particular type parameters?

If you want to get some special generic type, You can use a fake function to wrap it.

const wrapperFoo = () => foo<number>()
type Return = ReturnType<typeof wrapperFoo>

More complex demo

function getList<T>(): {
  list: T[],
  add: (v: T) => void,
  remove: (v: T) => void,
  // ...blahblah
}
const wrapperGetList = () => getList<number>()
type List = ReturnType<typeof wrapperGetList>
// List = {list: number[], add: (v: number) => void, remove: (v: number) => void, ...blahblah}

This is my currently working solution for extracting un-exported internal types of imported libraries (like knex):

// foo is an imported function that I have no control over
function foo<T>(e: T): InternalType<T> {
    return e;
}

class Wrapper<T> {
  // wrapped has no explicit return type so we can infer it
  wrapped(e: T) {
    return foo<T>(e)
  }
}

type FooInternalType<T> = ReturnType<Wrapper<T>['wrapped']>
type Y = FooInternalType<number>
// Y === InternalType<number>

I found a good and easy way to achieve this if you can change the function definition. In my case, I needed to use the typescript type Parameters with a generic function, precisely I was trying Parameters<typeof foo<T>> and effectively it doesn't work. So the best way to achieve this is changing the function definition by an interface function definition , this also will work with the typescript type ReturnType .

Here an example following the case described by the OP:

function foo<T>(e: T): T {
   return e;
}

type fooReturn = ReturnType<typeof foo<number>>; // Damn! it throws error

// BUT if you try defining your function as an interface like this:

interface foo<T>{
   (e: T): T
}

type fooReturn = ReturnType<foo<number>> //it's number, It works!!!
type fooParams = Parameters<foo<string>> //it also works!! it is [string]

//and you can use the interface in this way
const myfoo: foo<number> = (asd: number) => {
    return asd;
};

myfoo(7);
const wrapperFoo = (process.env.NODE_ENV === 'development' ? foo<number>() : undefined)!
type Return = typeof wrapperFoo

Instead of development for NODE_ENV I would even use test or some random string like typescript_helper (just as long as TypeScript doesn't throw an error) so that it never executes since it won't be used in runtime.

I found a solution. You decide if it fits your needs :)

Declare your function args and return type using a interface

interface Foo<T, V> {
  (t: T, v: V): [T, V]
}

Implement your function this way using Parameters and ReturnType

function foo<T, V>(...[t, v]: Parameters<Foo<T, V>>): ReturnType<Foo<T, V>> {
  return [t, v]; // [T, V]
}

Call your function normally, or get return type using ReturnType

foo(1, 'a') // [number, string]
type Test = ReturnType<Foo<number, number>> // [number, number]

TypeScript compiler does not see typeof foo as generic type. I'd say it's a bug in the compiler.

However, TypeScript has callable interfaces which can be generic without any problems, so if you introduce a callable interface compatible with the signature of your function, you can implement your own equivalent of ReturnType like this:

function foo<T>(x: T): T {
  return x;
}


interface Callable<R> {
  (...args: any[]): R;
}

type GenericReturnType<R, X> = X extends Callable<R> ? R : never;

type N = GenericReturnType<number, typeof foo>; // number

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM