Mr Josselin A - TypeScript









Fonctions

Références

L'actualité

Librairie

L'information

Introduction

Les fonctions sont la pierre angulaire de toutes les applications en JavaScript. C'est ainsi que vous construisez des couches d'abstraction, imitant des classes, masquant des informations et des modules. Dans TypeScript, bien qu'il existe des classes, des espaces de noms et des modules, les fonctions jouent toujours un rôle clé dans la description de la procédure à suivre. TypeScript ajoute également de nouvelles fonctionnalités aux fonctions JavaScript standard pour faciliter leur utilisation.

Les fonctions

Pour commencer, comme en JavaScript, les fonctions TypeScript peuvent être créées à la fois en tant que fonction nommée ou en tant que fonction anonyme. Cela vous permet de choisir l'approche la plus appropriée pour votre application, que vous construisiez une liste de fonctions dans une API ou une fonction ponctuelle à transférer à une autre fonction.

Pour récapituler rapidement à quoi ressemblent ces deux approches en JavaScript :

Tout comme en JavaScript, les fonctions peuvent faire référence à des variables extérieures au corps de la fonction. Quand ils le font, on leur dit de capturer ces variables. Bien que comprendre comment cela fonctionne et quels compromis utiliser avec cette technique sortent du cadre de cet article, il est bien compris à quel point ce mécanisme est une étape importante du travail avec JavaScript et TypeScript.


Types de fonction

Taper la fonction

Ajoutons des types à nos exemples simples précédents :

Nous pouvons ajouter des types à chacun des paramètres, puis à la fonction elle-même pour ajouter un type de retour. TypeScript peut comprendre le type de retour en examinant les instructions return, ce qui permet de le laisser éventuellement dans de nombreux cas.

Ecrire le type de fonction

Maintenant que nous avons saisi la fonction, écrivons le type complet de la fonction en regardant chaque élément du type de fonction.

Le type d'une fonction comporte les deux mêmes parties : le type des arguments et le type de retour. Lors de l'écriture du type de fonction complet, les deux parties sont nécessaires. Nous écrivons les types de paramètres comme une liste de paramètres, en attribuant à chaque paramètre un nom et un type. Ce nom est juste pour aider à la lisibilité. Nous aurions pu écrire à la place :

Tant que les types de paramètres sont alignés, il est considéré comme un type valide pour la fonction, quels que soient les noms que vous attribuez aux paramètres dans le type de fonction.

La deuxième partie est le type de retour. Nous précisons le type de retour en utilisant une grosse flèche (=>) entre les paramètres et le type de retour. Comme mentionné précédemment, il s'agit d'une partie obligatoire du type de fonction. Par conséquent, si la fonction ne renvoie pas de valeur, vous utiliserez void au lieu de la laisser désactivée.

Il est à noter que seuls les paramètres et le type de retour constituent le type de fonction. Les variables capturées ne sont pas reflétées dans le type. En effet, les variables capturées font partie de "l'état caché" de toute fonction et ne constituent pas son API.

Déduire les types

En jouant avec l'exemple, vous remarquerez peut-être que le compilateur TypeScript peut déterminer le type si vous avez des types d'un côté de l'équation mais pas de l'autre.

C'est ce qu'on appelle le "typage contextuel", une forme d'inférence de type. Cela aide à réduire la quantité d'effort pour garder votre programme dactylographié.

Paramètres facultatifs et par dèfaut

Dans TypeScript, chaque paramètre est supposé être requis par la fonction. Cela ne signifie pas qu'il ne peut pas être donné comme valeur null ou undefined, mais lorsque la fonction est appelée, le compilateur vérifie que l'utilisateur a fourni une valeur pour chaque paramètre. Le compilateur suppose également que ces paramètres sont les seuls paramètres qui seront transmis à la fonction. En bref, le nombre d'arguments donnés à une fonction doit correspondre au nombre de paramètres attendus par la fonction.

En JavaScript, chaque paramètre est facultatif et les utilisateurs peuvent les laisser comme ils l'entendent. Quand ils le font, leur valeur est indéfinie. Nous pouvons obtenir cette fonctionnalité dans TypeScript en ajoutant un ? à la fin des paramètres, nous voulons être facultatifs. Par exemple, supposons que nous voulions que le paramètre de nom de famille ci-dessus soit optionnel :

Tous les paramètres facultatifs doivent suivre les paramètres requis. Si nous avions voulu rendre le prénom facultatif plutôt que le nom de famille, nous aurions besoin de changer l'ordre des paramètres dans la fonction, en plaçant le prénom en dernier dans la liste.

Dans TypeScript, nous pouvons également définir une valeur à laquelle un paramètre sera attribué si l'utilisateur n'en fournit pas, ou si l'utilisateur passe non défini à sa place. Ceux-ci sont appelés paramètres initialisés par défaut. Prenons l'exemple précédent et définissons par défaut le nom de famille sur "Smith".

Les paramètres par défaut initialisés après que tous les paramètres requis sont traités comme facultatifs et peuvent, tout comme les paramètres facultatifs, être omis lors de l'appel de leur fonction respective. Cela signifie que les paramètres facultatifs et les paramètres par défaut de fin partagent les mêmes caractéristiques, de sorte que

et

partager le même type (firstName: string, lastName ?: string) => string. LastName, la valeur par défaut, disparaît dans le type, ne laissant que le fait que le paramètre soit facultatif.

Contrairement aux paramètres facultatifs simples, les paramètres initialisés par défaut n'ont pas besoin de se produire après les paramètres requis. Si un paramètre initialisé par défaut vient avant un paramètre obligatoire, les utilisateurs doivent explicitement passer undefined pour obtenir la valeur initialisée par défaut. Par exemple, nous pourrions écrire notre dernier exemple avec uniquement un initialiseur par défaut sur firstName :


Paramètres de repos

Les paramètres requis, facultatifs et par défaut ont tous une chose en commun : ils ne parlent que d'un paramètre à la fois. Parfois, vous souhaitez travailler avec plusieurs paramètres en tant que groupe ou vous pouvez ne pas savoir combien de paramètres une fonction prendra finalement. En JavaScript, vous pouvez utiliser les arguments directement à l'aide de la variable arguments visible dans chaque corps de fonction.

Dans TypeScript, vous pouvez rassembler ces arguments dans une variable :

Les paramètres de repos sont traités comme un nombre illimité de paramètres facultatifs. Lorsque vous transmettez des arguments pour un paramètre rest, vous pouvez en utiliser autant que vous le souhaitez vous pouvez même n'en passer aucun. Le compilateur construira un tableau des arguments passés avec le nom donné après les points de suspension (...), ce qui vous permettra de l'utiliser dans votre fonction.

Les points de suspension sont également utilisés dans le type de la fonction avec des paramètres de repos :


this

Apprendre à utiliser this en JavaScript est en quelque sorte un rite de passage. Comme TypeScript est un sur-ensemble de JavaScript, les développeurs de TypeScript doivent également apprendre à les utiliser et à détecter les cas où ils ne sont pas utilisés correctement. Heureusement, TypeScript vous permet de détecter les utilisations incorrectes à l'aide de plusieurs techniques.

this & arrow functions

En JavaScript, il s'agit d'une variable définie lors de l'appel d'une fonction. Cela en fait une fonctionnalité très puissante et flexible, mais au prix de toujours avoir à connaître le contexte dans lequel une fonction s'exécute. Cela est particulièrement déroutant, en particulier lorsque vous renvoyez une fonction ou que vous lui transmettez une argumentation.

Regardons un exemple:


Notez que createCardPicker est une fonction qui renvoie elle-même une fonction. Si nous essayions d'exécuter l'exemple, nous obtiendrions une erreur au lieu du message d'alerte attendu. Cela est dû au fait que cet objet utilisé dans la fonction créée par createCardPicker sera défini sur window au lieu de notre objet deck. C'est parce que nous appelons cardPicker() seul. Un appel de syntaxe de niveau supérieur sans méthode comme ceci utilisera window pour cela. (Remarque: en mode strict, ce sera non défini plutôt que fenêtre).

Nous pouvons résoudre ce problème en nous assurant que la fonction est liée au correct avant de renvoyer la fonction à utiliser ultérieurement. De cette façon, quelle que soit l'utilisation ultérieure, il pourra toujours voir l'objet de pont original. Pour ce faire, nous modifions l'expression de la fonction afin qu'elle utilise la syntaxe ECMAScript 6 arrow. Les fonctions fléchées capturent le présent où la fonction est créée plutôt que où elle est appelée :

Mieux encore, TypeScript vous avertira lorsque vous faites cette erreur si vous passez l'indicateur --noImplicitThis au compilateur. Il est à noter que ceci dans this.suits [pickSuit] est du type any.

Paramètres this

Malheureusement, le type de this.suits [pickSuit] est encore inconnu. C'est parce que cela vient de l'expression de la fonction dans le littéral de l'objet. Pour résoudre ce problème, vous pouvez fournir un paramètre explicite à ce paramètre, ces paramètres sont de faux paramètres qui apparaissent en premier dans la liste de paramètres d'une fonction :

Ajoutons quelques interfaces à notre exemple ci-dessus, Card and Deck, pour rendre les types plus clairs et plus faciles à réutiliser :

Désormais, TypeScript sait que createCardPicker s'attend à être appelé sur un objet Deck. Cela signifie que c'est de type Deck maintenant, pas n'importe lequel, donc --noImplicitThis ne causera aucune erreur.

Paramètres this & callbacks

Vous pouvez également rencontrer des erreurs avec cela dans les rappels, lorsque vous transmettez des fonctions à une bibliothèque qui les appellera ultérieurement. Parce que la bibliothèque qui appelle votre rappel l'appellera comme une fonction normale, cela sera indéfini. Avec certains travaux, vous pouvez également utiliser ces paramètres pour éviter les erreurs de rappel. Tout d'abord, l'auteur de la bibliothèque doit annoter le type de rappel avec ceci :

this: void signifie que addClickListener s'attend à ce que onclick soit une fonction ne nécessitant pas ce type. Deuxièmement, annotez votre code d'appel avec ceci:

Avec cette annotation, vous expliquez que onClickBad doit être appelé sur une instance de Handler. Ensuite, TypeScript détectera que addClickListener requiert une fonction qui a ceci : void. Pour corriger l'erreur, changez le type de ceci :

Comme onClickGood spécifie que ce type est vide, il est légal de passer à addClickListener. Bien entendu, cela signifie également qu'il ne peut pas utiliser this.info. Si vous voulez les deux, vous devrez utiliser une fonction de flèche:

Cela fonctionne parce que les fonctions de flèche ne capturent pas cela, vous pouvez donc toujours les transmettre à quelque chose qui attend cela : void. L'inconvénient est qu'une fonction de flèche est créée par objet de type Handler. Les méthodes, en revanche, ne sont créées qu'une fois et attachées au prototype de Handler. Ils sont partagés entre tous les objets de type Handler.

Les surcharges

JavaScript est par nature un langage très dynamique. Il n'est pas rare qu'une seule fonction JavaScript renvoie différents types d'objets en fonction de la forme des arguments transmis.

Ici, la fonction pickCard renverra deux choses différentes en fonction de ce que l'utilisateur a transmis. Si l'utilisateur passe dans un objet qui représente le deck, la fonction choisira la carte. Si l'utilisateur choisit la carte, nous lui indiquons quelle carte il a choisie. Mais comment décrivons-nous cela au système de types ?

La solution consiste à fournir plusieurs types de fonction pour la même fonction sous la forme d'une liste de surcharges. Cette liste est ce que le compilateur utilisera pour résoudre les appels de fonction. Créons une liste des surcharges décrivant ce que notre carte pickCard accepte et ce qu'elle renvoie.

Avec ce changement, les surcharges nous donnent maintenant des appels dactylographiés à la fonction pickCard.

Pour que le compilateur sélectionne le bon typecheck, il suit un processus similaire au JavaScript sous-jacent. Il examine la liste de surcharge et poursuit avec la première tentative d'appeler la fonction avec les paramètres fournis. S'il trouve une correspondance, il sélectionne cette surcharge comme étant la surcharge correcte. Pour cette raison, il est habituel de classer les surcharges du plus spécifique au moins spécifique.

Notez que la fonction pickCard(x) : toute pièce ne fait pas partie de la liste de surcharge, elle n'a donc que deux surcharges : une qui prend un objet et une qui prend un nombre. L'appel de pickCard avec tout autre type de paramètre provoquerait une erreur.