获取函数的返回类型

259

我有以下函数:

function test(): number {
    return 42;
}

我可以使用typeof获得函数的类型:

type t = typeof test;

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

有没有一种方法可以获取函数的返回类型?我希望 tnumber 而不是 () => number


4
不行,无论如何都不行。typeof 只会返回 "function" (我的大小写可能有误)。 - Igor
如果这变成可能的话,那就太好了。有时候你在函数中定义类型,但是没有(好的)方法来捕获这个结构。 - Jørgen Tvedt
3
简而言之,在2023年,答案很简单: type t = ReturnType<typeof test> - Wojciech Wisowaty
9个回答

403

编辑

从 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[]

请查看这个对Microsoft/TypeScript的拉取请求获取详细信息。
TypeScript非常棒!
老派黑客技巧 不幸的是,Ryan的答案已经不再适用。但我使用了一个技巧进行了修改,我对此感到非常高兴。瞧!
const fnReturnType = (false as true) && fn();

它的工作原理是将false转换为true的字面值,这样类型系统会认为返回值是函数的类型,但实际运行代码时,它会在false处短路。


2
@NSjonas 你需要告诉 VSCode 使用另一个版本。我想在右下角的状态栏上。 - Meirion Hughes
83
我需要进行 ReturnType<typeof fn>(答案暗示 ReturnType<fn> 即可)。 - spacek33z
1
有没有类似的方法可以获取函数参数的模拟? - tslater
3
你知道如何从 Promise<Type> 中提取 Type 吗?这是返回值的常见情况。 - YakovL
9
关于 Promises,它是 type promiseType = Awaited<ReturnType<typeof promiseFn>>; - dr497
显示剩余8条评论

146

在TypeScript 2.8中最简单的方法:

const foo = (): FooReturnType => {
}

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

1
接受答案的副本 - Jared Beach
4
直截了当且能立即实施的措施 +1 - walkingbrad

13

使用内置的ReturnType

type SomeType = ReturnType<typeof SomeFunc>

ReturnType扩展为:

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

5
下面的代码不需要执行该函数。它来自于 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;

3

编辑:这在TS 2.8版已不再需要!ReturnType<F>提供返回类型。请参阅接受的答案。


我正在使用一些先前答案上的变体,它可以在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的调用,但在我看来,这只会让事情更加混乱。 我刚刚使用了一些正则表达式的查找/替换方法来迁移一个旧的Angular 1.x项目,其中有大约1500个像这样编写的工厂函数,并在所有使用中添加了Foo等类型……惊人的是,你会发现一些破碎的代码。 :)

谢谢!虽然需要这么多的措辞让人感到悲伤,但至少它能够工作! - ecmanaut
@ecmanaut,我们不再需要这个了!在TS 2.8中,您可以使用ReturnType<F>来获取函数的返回类型。 :) - Aaron Beall
这里的答案与问题的示例基础大相径庭,使得很难弄清楚如何生成一个与函数“test”的返回类型相同的类型。这个答案是其中一个更有用的答案,只需将“test”重命名为“foo”(并且承认为了好玩而产生了更复杂的返回类型)。如果您已经知道如何将它们应用于原始示例,则接受的答案和您的评论可能都很棒。(现在是深夜,在我看来,完整的ReturnType示例的语法不是立即显而易见的。) - ecmanaut

3

目前还没有一种方法可以做到这一点(请参见https://github.com/Microsoft/TypeScript/issues/6606以查看跟踪添加此功能的工作项)。

一个常见的解决方法是编写类似以下内容的代码:

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

4
我认为这个变通方法不再有效 - 很可能是因为基于控制流的类型分析。 - JKillian
3
@JKillian,您是正确的,提出的示例不再有效,尽管您可以使用!1而不是false很容易地欺骗TypeScript-- http://www.typescriptlang.org/play/#src=function%20f()%3A%20string%20%7B%0A%20return%20%2742%27%0A%7D%0A%0Alet%20a%20%3D%20!1%20%26%26%20f()%0Alet%20b%3A%20typeof%20a%20%3D%20%27abc%27 - DanielM
ReturnType 应该是答案。 - Jared Beach
看起来你想要的是!0而不是!1。这使得上面的例子对我有效。话虽如此,这种方法的一个副作用是你需要调用test()函数才能获取类型。如果test是一个有副作用或者开销很大的函数,那么这将导致错误或性能问题。 - KhalilRavanna

2
如果所讨论的函数是用户定义类的方法,您可以使用方法装饰器以及Reflect Metadata来确定在运行时的返回类型(构造函数),并根据需要进行操作。例如,您可以将其记录到控制台中:
function logReturnType(
    target: Object | Function,
    key: string,
    descriptor: PropertyDescriptor
): PropertyDescriptor | void {
    var returnType = Reflect.getMetadata("design:returntype", target, key);

    console.log(returnType);
}

只需将此方法装饰器添加到您选择的方法上,您就可以获得从该方法调用中应该返回的对象的构造函数的确切引用。

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

然而,这种方法有一些值得注意的限制:

  • 在使用装饰器修饰方法时,您需要明确定义返回类型,否则将从Reflect.getMetadata获得未定义的结果。
  • 您只能引用编译后仍存在的实际类型;也就是说,不能使用接口或泛型。

此外,在指定以下命令行参数进行 TypeScript 编译时,因为装饰器和反射元数据都是实验性功能,所以需要特别注意:

--emitDecoratorMetadata --experimentalDecorators

ReturnType 应该是答案。 - Jared Beach

0
我想出了以下的解决方案,看起来很好用:
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

1
我认为函数参数不需要是泛型的,因为你无论如何都会抛弃它们。fn: (...args: any[]) => Z 就足够了。 - Aaron Beall

0
如果你有一个类,并且想要获取一个方法的返回类型,可以使用以下方法:
class MeaningOfLife {
  public get() {
    return 42 as const;
  }
}

您可以访问该类的原型以将方法作为函数获取。
export type Answer = ReturnType<typeof MeaningOfLife.prototype.get>; // equals the literal 42

这很不错,因为有时候TypeScript能够通过分析代码来推断复杂的类型。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接