获取函数的 return 类型

Obtaining the return type of a function

我有以下功能:

function test(): number {
    return 42;
}

我可以通过typeof:

获取函数的类型
type t = typeof test;

在这里,t 将是 () => number

有没有办法获取函数的return类型?我希望 t 成为 number 而不是 () => number

没有办法做到这一点(请参阅 https://github.com/Microsoft/TypeScript/issues/6606 了解添加此功能的工作项跟踪)。

一个常见的解决方法是这样写:

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

如果有问题的函数是用户定义的方法 class,您可以使用 method decorators in conjuction with Reflect Metadata 来确定 return 类型(构造函数)在 runtime(并随心所欲地做)。

例如,您可以将其记录到控制台:

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

    console.log(returnType);
}

只需将此方法装饰器捕捉到您选择的方法上,您就可以准确引用对象的构造函数,据推测,该对象是从方法调用中 return 编辑的。

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

但是,这种方法有一些明显的局限性:

  • 您需要在这样装饰的方法上显式定义 return 类型,否则您会从 Reflect.getMetadata
  • 中得到未定义
  • 您只能引用编译后也存在的实际类型;也就是说,没有接口或泛型

此外,您还需要为打字稿编译器指定以下命令行参数,因为装饰器和反射元数据在撰写本文时都是实验性功能 post:

--emitDecoratorMetadata --experimentalDecorators

编辑

从 TypeScript 2.8 开始,这在 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[]

详情见this pull request to Microsoft/TypeScript

TypeScript 很棒!


老派黑客

不幸的是,Ryan 的回答不再有效。但是我用一个让我非常高兴的 hack 修改了它。看:

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

它的工作原理是将 false 转换为 true 的字面值,这样类型系统就认为 return 值是函数的类型,但是当你实际上 运行 代码,它在 false.

上短路

我想出了以下方法,似乎效果很好:

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

下面的代码在不执行函数的情况下工作。它来自 react-redux-typescript 库 (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;

编辑: TS 2.8 不再需要它! ReturnType<F> 给出 return 类型。查看已接受的答案。


我正在使用的一些先前答案的变体,它在 strictNullChecks 中有效并稍微隐藏了推理体操:

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

用法:

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; }

确实调用了getReturnType函数,但调用了原函数。您 可以 使用 (false as true) && getReturnType(foo) 阻止 getReturnType 调用,但在我看来,这只会让事情变得更加混乱。

我只是用这个方法和一些正则表达式 find/replace 来迁移一个旧的 Angular 1.x 项目,它有大约 1500 个这样写的工厂函数,最初是在 JS 中,并添加了Foo 等类型的所有用途...人们会发现破损的代码令人惊奇。 :)

TypeScript 2.8 中最简单的方法:

const foo = (): FooReturnType => {
}

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

使用built-inReturnType:

type SomeType = ReturnType<typeof SomeFunc>

ReturnType 扩展为:

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