rid
rid

Reputation: 63442

Obtaining the return type of a function

I have the following function:

function test(): number {
    return 42;
}

I can obtain the type of the function by using typeof:

type t = typeof test;

Here, t will be () => number.

Is there a way to obtain the return type of the function? I would like t to be number instead of () => number.

Upvotes: 312

Views: 230779

Answers (9)

Christian Vincenzo Traina
Christian Vincenzo Traina

Reputation: 10384

Just in case you have a class and you want to get the return type of a method:

class MeaningOfLife {
  public getMeaningOfLife() { // No explicit return type
    return 42 as const;
  }
}

You can access the prototype of the class in order to get the method as a function:

export type Answer = ReturnType<typeof MeaningOfLife.prototype.getMeaningOfLife>; // equals the literal 42

This is pretty nice because with little code, TypeScript will be able to infer even more complex types

Upvotes: 5

z0gSh1u
z0gSh1u

Reputation: 507

Use built-in ReturnType:

type SomeType = ReturnType<typeof SomeFunc>

ReturnType expands to:

type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;

Upvotes: 24

thedayturns
thedayturns

Reputation: 10823

EDIT

As of TypeScript 2.8 this is officially possible with ReturnType<T>.

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]

See this pull request to Microsoft/TypeScript for details.

TypeScript is awesome!


Old-school hack

Ryan's answer doesn't work anymore, unfortunately. But I have modified it with a hack which I am unreasonably happy about. Behold:

const fnReturnType = (false as true) && fn();

It works by casting false to the literal value of true, so that the type system thinks the return value is the type of the function, but when you actually run the code, it short circuits on false.

Upvotes: 467

Jaroslav
Jaroslav

Reputation: 2303

The easiest way in the TypeScript 2.8:

const foo = (): FooReturnType => {
}

type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType

Upvotes: 181

Aaron Beall
Aaron Beall

Reputation: 52143

Edit: This is not needed with TS 2.8 any more! ReturnType<F> gives the return type. See accepted answer.


A variant on some of the previous answers that I'm using, which works in strictNullChecks and hides the inference gymnastics a bit:

function getReturnType<R>(fn: (...args: any[]) => R): R {
  return {} as R;
}

Usage:

function foo() {
  return {
    name: "",
    bar(s: string) { // doesn't have to be shorthand, could be `bar: barFn` 
      return 123;
    }
  }
}

const _fooReturnType = getReturnType(foo);
export type Foo = typeof _fooReturnType; // type Foo = { name: string; bar(s: string): number; }

It does call the getReturnType function, but it does not call the original function. You could prevent the getReturnType call using (false as true) && getReturnType(foo) but IMO this just makes it more confusing.

I just used this method with some regexp find/replace to migrate an old Angular 1.x project that had ~1500 factory functions written like this, originally in JS, and added the Foo etc types to all uses... amazing the broken code one will find. :)

Upvotes: 4

eug
eug

Reputation: 1158

The code below works without executing the function. It's from the react-redux-typescript library (https://github.com/alexzywiak/react-redux-typescript/blob/master/utils/redux/typeUtils.ts)

interface Func<T> {
    ([...args]: any, args2?: any): T;
}
export function returnType<T>(func: Func<T>) {
    return {} as T;
}


function mapDispatchToProps(dispatch: RootDispatch, props:OwnProps) {
  return {
    onFinished() {
      dispatch(action(props.id));
    }
  }
}

const dispatchGeneric = returnType(mapDispatchToProps);
type DispatchProps = typeof dispatchGeneric;

Upvotes: 6

Werner de Groot
Werner de Groot

Reputation: 977

I came up with the following, which seems to work nicely:

function returnType<A, B, Z>(fn: (a: A, b: B) => Z): Z
function returnType<A, Z>(fn: (a: A) => Z): Z
function returnType<Z>(fn: () => Z): Z
function returnType(): any {
    throw "Nooooo"
}

function complicated(value: number): { kind: 'complicated', value: number } {
    return { kind: 'complicated', value: value }
}

const dummy = (false as true) && returnType(complicated)
type Z = typeof dummy

Upvotes: 0

John Weisz
John Weisz

Reputation: 31924

If the function in question is a method of a user defined class, you can use method decorators in conjuction with Reflect Metadata to determine the return type (constructor function) at runtime (and with it, do as you see fit).

For example, you could log it to the console:

function logReturnType(
    target: Object | Function,
    key: string,
    descriptor: PropertyDescriptor
): PropertyDescriptor | void {
    var returnType = Reflect.getMetadata("design:returntype", target, key);

    console.log(returnType);
}

Just snap this method decorator on a method of your choice and you have the exact reference to the constructor function of the object that is supposedly returned from the method call.

class TestClass {
    @logReturnType // logs Number (a string representation)
    public test(): number {
        return 42;
    }
}

There are a few notable limitations to this approach, however:

  • you need to explicitly define the return type on a method decorated as such, otherwise you'll get undefined from Reflect.getMetadata,
  • you can only reference actual types which also exist after compilation; that is, no interfaces or generics

Also, you'll need to specify the following command line arguments for the typescript compiler, because both decorators and reflect metadata are experimental features as of writing this post:

--emitDecoratorMetadata --experimentalDecorators

Upvotes: 3

Ryan Cavanaugh
Ryan Cavanaugh

Reputation: 220964

There isn't a way to do this (see https://github.com/Microsoft/TypeScript/issues/6606 for the work item tracking adding this).

A common workaround is write something like:

var dummy = false && test();
type t2 = typeof dummy;

Upvotes: 4

Related Questions