ProgrammationDéveloppeur Frontend/Fullstack

Comment fonctionne le type ReturnType<T> dans TypeScript, en quoi diffère-t-il de l'inférence manuelle du type de valeur de retour d'une fonction, et quels sont les risques/bénéfices de son utilisation ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Historique de la question

Avec le développement de TypeScript, il est devenu nécessaire d'extraire automatiquement le type de valeur de retour d'une fonction, surtout pour les grands projets avec de nombreuses fonctions interconnectées. Pour cela, le type utilitaire ReturnType<T> a été introduit dans la bibliothèque standard à partir de la version TypeScript 2.8.

Problème

Dans de grands et complexes projets, il peut être difficile de maintenir l'actualité des types - si l'on indique manuellement le type de retour de chaque fonction et qu'en même temps on modifie les signatures, il est facile d'obtenir un désaccord lorsque les signatures et les implémentations ne correspondent pas. De plus, si une fonction retourne une structure complexe, il n'est pas toujours facile de synchroniser manuellement la description du type de retour à tous les endroits d'utilisation.

Solution

Le type utilitaire ReturnType<T> extrait automatiquement le type de retour des fonctions et peut être utilisé pour typer le résultat d'un appel de fonction à n'importe quel endroit du code. Cela réduit le besoin de maintenance manuelle de l'infrastructure de type et minimise les erreurs liées à l'inadéquation entre le type décrit et le type réel de la valeur de retour.

Exemple de code :

function createUser(name: string, age: number) { return { name, age, created: new Date() }; } type User = ReturnType<typeof createUser>; // User: { name: string; age: number; created: Date; }

Caractéristiques clés :

  • Extrait automatiquement le type de retour des fonctions (y compris les génériques), éliminant la duplication de code.
  • Réduit la probabilité d'erreur lors des modifications de la signature d'une fonction ou de la structure de la valeur de retour.
  • Ne fonctionne pas avec les surcharges de fonctions - extrait uniquement le type de retour général (large).

Questions piégées.

Peut-on utiliser ReturnType avec les méthodes des classes ?

Oui, mais il est important de se souvenir du contexte : si la méthode est une propriété d'un objet avec une fonction, utilisez ReturnType<obj['méthode']>.

Exemple de code :

class MyClass { foo(x: number) { return x * 2; } } type FooReturn = ReturnType<MyClass['foo']>; // Erreur de type ! // Il faut faire ainsi : type FooReturn = ReturnType<(x: number) => number>; // number // Ou extraire la méthode comme fonction : const obj = new MyClass(); type FooReturn2 = ReturnType<typeof obj.foo>;

Que renverra ReturnType pour des fonctions avec void/never ?

Pour une fonction avec le type déclaré void, ReturnType renverra void. Pour never - never.

Exemple de code :

function doNothing(): void {} type Result = ReturnType<typeof doNothing>; // void

ReturnType fonctionne-t-il avec les surcharges de fonctions ?

Non, ReturnType extrait le type de retour de la "réalisation" elle-même, et non de toutes les surcharges. Si plusieurs surcharges existent, le type de la réalisation décrite est pris en compte.

Exemple de code :

function func(x: number): number; function func(x: string): string; function func(x: any): any { return x } type RT = ReturnType<typeof func>; // any

Erreurs typiques et anti-patterns

  • L'utilisation de ReturnType avec des fonctions surchargées conduit à des types inattendus.
  • Oublier que ReturnType ne calcule pas le type de retour d'une valeur Promise - Awaited ou un travail manuel est nécessaire.
  • Compter entièrement sur ReturnType lors de changements de la logique d'une fonction sans mettre à jour d'autres parties dépendantes du code.

Exemple concret

Cas négatif

Dans un projet, un type manuel est déclaré pour l'objet retourné par une fonction. La fonction change - le type n'est pas mis à jour, les appels échouent à l'exécution.

Avantages :

  • Les types sont faciles à lire, on peut les ajuster manuellement.

Inconvénients :

  • Ils se démodent rapidement, un "dérive" se crée entre les types et l'API réelle.

Cas positif

On passe à ReturnType partout où on utilise la valeur retournée par la fonction. En cas de modification, le type est toujours à jour.

Avantages :

  • Duplicata minimal, correspondance typée avec la réalisation réelle.

Inconvénients :

  • Des difficultés peuvent surgir concernant la magie des types pour les débutants.