Obter o tipo de retorno de uma função


95

Tenho a seguinte função:

function test(): number {
    return 42;
}

Posso obter o tipo da função usando typeof:

type t = typeof test;

Aqui, tvai ser () => number.

Existe uma maneira de obter o tipo de retorno da função? Eu gostaria tde ser em numbervez de () => number.


2
Não, não com typeof de qualquer maneira. typeof retornará apenas "função" ( minha caixa pode estar errada ).
Igor

Seria ótimo se isso fosse possível. Às vezes, você define tipos conforme os empurra para fora de uma função, e não há uma maneira (boa) de capturar essa estrutura.
Jørgen Tvedt

Respostas:


151

EDITAR

A partir do TypeScript 2.8, isso é oficialmente possível com 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[]

Veja aqui os detalhes.

TypeScript é incrível!


Hack da velha escola

A resposta de Ryan não funciona mais, infelizmente. Mas eu o modifiquei com um hack que me deixou extremamente feliz. Ver:

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

Ele funciona convertendo false para o valor literal de true, de modo que o sistema de tipo pense que o valor de retorno é o tipo da função, mas quando você realmente executa o código, ele causa um curto-circuito em false.

TypeScript é o melhor. : D


48
Jesus, isso é horrível.
John Weisz

para fluxo: const DUMMY = ((null: any): Object) && fn () export type Type = typeof DUMMY
David Xu

alguma ideia porque quando eu instalo 2.8 e tento recebo o seguinte erro Cannot find name 'ReturnType':? usando vscode
NSjonas

1
@NSjonas você precisa dizer ao vscode para usar a outra versão. Acho que na barra de status no canto inferior direito.
Meirion Hughes de

11
Eu tive que fazer ReturnType<typeof fn>(a resposta implica ReturnType<fn>é o suficiente).
spacek33z

45

A maneira mais fácil no TypeScript 2.8:

const foo = (): FooReturnType => {
}

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

5

O código abaixo funciona sem executar a função. É da biblioteca 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;

4

Não há uma maneira de fazer isso (consulte https://github.com/Microsoft/TypeScript/issues/6606 para o rastreamento de item de trabalho adicionando isso).

Uma solução alternativa comum é escrever algo como:

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

2
Não acho que essa solução alternativa funcione mais - provavelmente por causa da análise de tipo baseada no fluxo de controle.
JKillian

2
@JKillian você está certo, o exemplo proposto não funciona mais, embora você possa facilmente enganar o TypeScript em !1vez de false- typescriptlang.org/play/…
DanielM

3

Edit: Isso não é mais necessário com o TS 2.8! ReturnType<F>fornece o tipo de retorno. Veja a resposta aceita.


Uma variante de algumas das respostas anteriores que estou usando, que funciona strictNullCheckse esconde um pouco a ginástica de inferência:

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

Uso:

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

Ele não chamar a getReturnTypefunção, mas não chamar a função original. Você poderia evitar a getReturnTypechamada usando, (false as true) && getReturnType(foo)mas IMO, isso só torna mais confuso.

Acabei de usar este método com algum regexp localizar / substituir para migrar um projeto Angular 1.x antigo que tinha ~ 1500 funções de fábrica escritas assim, originalmente em JS, e adicionei os Footipos etc para todos os usos ... incrível o código quebrado vai encontrar. :)


Obrigado! Embora seja triste levar essa quantidade de verborragia, pelo menos funciona!
ecmanaut

@ecmanaut Não precisamos mais disso! No TS 2.8 você pode usar ReturnType<F>para obter um tipo de retorno de funções. :)
Aaron Beall

As respostas aqui divergem muito do substrato de exemplo da pergunta, tornando difícil descobrir como produzir um tipo que seja o tipo retornado da função test. Essa resposta foi uma das mais úteis para apenas renomear testpara foo(e, reconhecidamente, produzir um tipo de retorno mais complexo, para variar). Tanto a resposta aceita quanto seu comentário são provavelmente ótimos se você já sabe como aplicá-los ao exemplo original. (É tarde da noite aqui e não é imediatamente aparente para mim como seria a sintaxe de um exemplo ReturnType completo.)
ecmanaut

1

Se a função em questão for um método de uma classe definida pelo usuário, você pode usar decoradores de método em conjunto com Reflect Metadata para determinar o tipo de retorno (função de construtor) em tempo de execução (e com ela, faça o que achar necessário).

Por exemplo, você pode registrá-lo no console:

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

    console.log(returnType);
}

Basta ajustar esse decorador de método em um método de sua escolha e você terá a referência exata à função construtora do objeto que supostamente é retornado da chamada do método.

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

No entanto, existem algumas limitações notáveis ​​para essa abordagem:

  • você precisa definir explicitamente o tipo de retorno em um método decorado como tal, caso contrário, você obterá indefinido de Reflect.getMetadata,
  • você só pode fazer referência a tipos reais que também existem após a compilação; ou seja, sem interfaces ou genéricos

Além disso, você precisará especificar os seguintes argumentos de linha de comando para o compilador de texto digitado, porque os decoradores e os metadados refletidos são recursos experimentais no momento da redação desta postagem:

--emitDecoratorMetadata --experimentalDecorators

0

Infelizmente, não há como recuperar o tipo de retorno da função sem executá-lo. Isso ocorre porque, quando o TypeScript é compilado em JS, você perde todas as informações relacionadas ao tipo.


3
Embora seja tecnicamente verdade que o TS perde informações de tipo durante a execução, esse fato não é relevante, porque o OP gostaria de determinar o tipo da função em tempo de compilação. Isso é ainda mais óbvio agora que ReturnType<T>foi implementado no TypeScript.
reviravoltas de

0

Eu descobri o seguinte, que parece funcionar bem:

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
Não acho que a função args precise ser genérica, já que você a está descartando de qualquer maneira. fn: (...args: any[]) => Zé tudo o que você precisa.
Aaron Beall
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.