Obtener el tipo de retorno de una función


101

Tengo la siguiente función:

function test(): number {
    return 42;
}

Puedo obtener el tipo de función usando typeof:

type t = typeof test;

Aquí testará () => number.

¿Hay alguna forma de obtener el tipo de retorno de la función? Me gustaría tser en numberlugar de () => number.


2
No, no con typeof de todos modos. typeof solo devolverá "función" ( mi carcasa puede estar incorrecta ).
Igor

Sería genial si esto fuera posible. A veces, define tipos a medida que los elimina de una función, y no hay una forma (agradable) de capturar esta estructura.
Jørgen Tvedt

Respuestas:


164

EDITAR

A partir de TypeScript 2.8, esto es oficialmente posible con 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[]

Consulte aquí para obtener más detalles.

¡TypeScript es increíble!


Hack de la vieja escuela

La respuesta de Ryan ya no funciona, desafortunadamente. Pero lo modifiqué con un truco que me alegra irrazonablemente. Mirad:

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

Funciona al convertir falso al valor literal de verdadero, de modo que el sistema de tipos piensa que el valor de retorno es el tipo de la función, pero cuando realmente ejecuta el código, se cortocircuita en falso.

TypeScript es el mejor. :RE


53
Jesús, eso es horrible.
John Weisz

para flujo: const DUMMY = ((nulo: cualquiera): Objeto) && fn () tipo de exportación Tipo = tipo de DUMMY
David Xu

¿Alguna idea de por qué cuando instalo 2.8 y lo intento, aparece el siguiente error Cannot find name 'ReturnType':? usando vscode
NSjonas

1
@NSjonas debes decirle a vscode que use la otra versión. Creo que en la barra de estado en la parte inferior derecha.
Meirion Hughes

12
Tenía que hacer ReturnType<typeof fn>(la respuesta implica que ReturnType<fn>es suficiente).
spacek33z

49

La forma más sencilla en TypeScript 2.8:

const foo = (): FooReturnType => {
}

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

5

El siguiente código funciona sin ejecutar la función. Es de la 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

No hay una manera de hacer esto (consulte https://github.com/Microsoft/TypeScript/issues/6606 para el seguimiento del elemento de trabajo que agrega esto).

Una solución alternativa común es escribir algo como:

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

3
No creo que esta solución funcione más, probablemente debido al análisis de tipo basado en el flujo de control.
JKillian

3
@JKillian tiene razón, el ejemplo propuesto ya no funciona, aunque puede engañar fácilmente a TypeScript con en !1lugar de false- typescriptlang.org/play/…
DanielM

3

Editar: ¡ Esto ya no es necesario con TS 2.8! ReturnType<F>da el tipo de retorno. Ver respuesta aceptada.


Una variante de algunas de las respuestas anteriores que estoy usando, que funciona strictNullChecksy oculta un poco la gimnasia de inferencia:

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

Se hace llamar a la getReturnTypefunción, pero no llama a la función original. Usted podría evitar que la getReturnTypellamada usando (false as true) && getReturnType(foo)pero En mi opinión esto sólo lo hace más confuso.

Acabo de usar este método con un poco de búsqueda / reemplazo de expresiones regulares para migrar un antiguo proyecto Angular 1.x que tenía ~ 1500 funciones de fábrica escritas así, originalmente en JS, y agregué los Footipos, etc. a todos los usos ... asombroso el código roto uno encontrará. :)


¡Gracias! Si bien es triste, requiere esta cantidad de verborrea, ¡al menos funciona!
ecmanaut

@ecmanaut ¡Ya no necesitamos esto! En TS 2.8 puede usar ReturnType<F>para obtener un tipo de retorno de funciones. :)
Aaron Beall

Las respuestas aquí difieren mucho del sustrato de ejemplo de la pregunta, lo que dificulta descubrir cómo producir un tipo que sea el tipo devuelto de la función test. Esta respuesta fue una de las más útiles para solo cambiar el nombre testa foo(y, ciertamente, producir un tipo de retorno más complejo, por diversión). Tanto la respuesta aceptada como su comentario probablemente sean excelentes si ya sabe cómo aplicarlos al ejemplo original. (Es tarde en la noche aquí, y no me parece inmediatamente evidente cómo se vería la sintaxis de un ejemplo completo de ReturnType.)
ecmanaut

1

Si la función en cuestión es un método de una clase definida por el usuario, puede usar decoradores de métodos junto con Reflect Metadata para determinar el tipo de retorno (función constructora) en tiempo de ejecución (y con él, hacer lo que crea conveniente).

Por ejemplo, puede registrarlo en la consola:

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

    console.log(returnType);
}

Simplemente coloque este decorador de métodos en un método de su elección y tendrá la referencia exacta a la función constructora del objeto que supuestamente se devuelve desde la llamada al método.

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

Sin embargo, existen algunas limitaciones notables para este enfoque:

  • necesita definir explícitamente el tipo de retorno en un método decorado como tal, de lo contrario, obtendrá indefinido de Reflect.getMetadata,
  • solo puede hacer referencia a tipos reales que también existen después de la compilación; es decir, sin interfaces ni genéricos

Además, deberá especificar los siguientes argumentos de línea de comando para el compilador de mecanografiado, porque tanto los decoradores como los metadatos reflect son características experimentales al momento de escribir esta publicación:

--emitDecoratorMetadata --experimentalDecorators

0

No hay forma de recuperar el tipo de retorno de la función sin ejecutarlo lamentablemente. Esto se debe a que cuando TypeScript se compila en JS, pierde toda la información sobre el tipo.


3
Aunque es técnicamente cierto que TS pierde información de tipo durante el tiempo de ejecución, este hecho no es relevante, porque a OP le gustaría determinar el tipo de función en el tiempo de compilación. Esto es aún más obvio ahora que ReturnType<T>se ha implementado en TypeScript.
cambia

0

Se me ocurrió lo siguiente, que parece funcionar muy bien:

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
No creo que los argumentos de la función necesiten ser genéricos, ya que los está desechando de todos modos. fn: (...args: any[]) => Zes todo lo que necesitas.
Aaron Beall
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.