web-dev-qa-db-fra.com

Obtention du type de retour d'une fonction

J'ai la fonction suivante:

function test(): number {
    return 42;
}

Je peux obtenir le type de la fonction en utilisant typeof:

type t = typeof test;

Ici, t sera () => number.

Existe-t-il un moyen d'obtenir le type de retour de la fonction? Je voudrais que t soit number au lieu de () => number.

56
rid

[~ # ~] éditer [~ # ~]

A partir de TypeScript 2.8, ceci est officiellement possible avec 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[]

Voir ici pour plus de détails.

TypeScript est génial!


bidouille de la vieille école

La réponse de Ryan ne fonctionne plus, malheureusement. Mais je l'ai modifié avec un bidouillage dont je suis déraisonnablement heureux. Voir:

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

Cela fonctionne en attribuant la valeur false à la valeur littérale de true, de sorte que le système de types pense que la valeur renvoyée est le type de la fonction. Toutefois, lorsque vous exécutez le code, il court-circuite False.

TypeScript est le meilleur. :RÉ

89
thedayturns

Le moyen le plus simple dans le TypeScript 2.8:

const foo = (): FooReturnType => {
}

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

Il n’ya pas de moyen de le faire (voir https://github.com/Microsoft/TypeScript/issues/6606 pour le suivi de l’élément de travail l’ajoutant).

Une solution courante consiste à écrire quelque chose comme:

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

Le code ci-dessous fonctionne sans exécuter la fonction. Il provient de la bibliothèque 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
eug

Edit: Ce n'est plus nécessaire avec TS 2.8! ReturnType<F> donne le type de retour. Voir la réponse acceptée.


Une variante de certaines des réponses précédentes que j'utilise, qui fonctionne dans strictNullChecks et cache un peu la gymnastique d'inférence:

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

Il appelle la fonction getReturnType, mais pas appelle la fonction d'origine. Vous pourriez empêcher l'appel getReturnType à l'aide de (false as true) && getReturnType(foo) _ mais IMO, cela ne fait que compliquer les choses.

Je viens d'utiliser cette méthode avec des expressions rationnelles (find/replace) pour migrer un ancien projet Angular 1.x) contenant environ 1 500 fonctions d'usine écrites de cette façon, à l'origine dans JS, et j'ai ajouté le Foo etc types pour tous les usages ... étonnant le code cassé on trouvera :)

2
Aaron Beall

Si la fonction en question est une méthode d'une classe définie par l'utilisateur, vous pouvez utiliser méthode décorateurs en conjonction avec métadonnées Reflect pour déterminer le type de retour (fonction constructeur) à runtime (et avec, faites comme bon vous semble).

Par exemple, vous pouvez le connecter à la console:

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

    console.log(returnType);
}

Attachez simplement ce décorateur de méthode sur une méthode de votre choix et vous aurez la référence exacte à la fonction constructeur de l’objet censé être renvoyé à partir de l’appel de la méthode.

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

Il existe toutefois quelques limitations notables à cette approche:

  • vous devez définir explicitement le type de retour sur une méthode décorée en tant que telle, sinon vous obtiendrez undefined de Reflect.getMetadata,
  • vous pouvez uniquement référencer des types réels qui existent également après la compilation; c'est-à-dire, pas d'interface ou de génériques

En outre, vous devrez spécifier les arguments de ligne de commande suivants pour le compilateur TypeScript, car les décorateurs et les métadonnées de réflexion sont des fonctionnalités expérimentales au moment de l'écriture de cet article:

--emitDecoratorMetadata --experimentalDecorators
1
John Weisz

Il n'y a aucun moyen de récupérer le type de retour de la fonction sans l'exécuter tristement. En effet, lorsque TypeScript est compilé dans JS, vous perdez toutes les informations concernant le type.

0
Luka Jacobowitz

Je suis arrivé avec ce qui suit, qui semble bien fonctionner:

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
0
Werner de Groot