Une fonction TypeScript est une fonction JavaScript avec des types sur ses paramètres et, si vous le souhaitez, sur sa valeur de retour. Chaque paramètre reçoit une annotation, le type de retour se place après la liste des paramètres, et le compilateur vérifie chaque appel par rapport aux deux.
Les deux mauvais appels sont des erreurs de compilation (TS2345 et TS2554). Les commentaires // @ts-expect-error indiquent au compilateur qu'une erreur est attendue à la ligne suivante, si bien que le reste du fichier s'exécute quand même. Retirez-en un et relancez pour voir le vrai message.
Types des paramètres
Chaque paramètre s'écrit name: Type. Avec strict, activé par défaut dans TypeScript 7, un paramètre sans annotation et sans contexte d'inférence est une erreur : Parameter 'x' implicitly has an 'any' type. (TS7006). En pratique, chaque paramètre d'une fonction autonome est donc annoté.
N'importe quel type peut servir de type de paramètre : primitives, tableaux, types objet, unions, autres fonctions.
TypeScript vérifie aussi le nombre d'arguments. En passer plus ou moins que ce que déclare la fonction est une erreur, contrairement au JavaScript pur, qui remplit les manquants avec undefined et ignore les surplus. Pour rendre un paramètre optionnel ou lui donner une valeur par défaut, voir paramètres optionnels.
Types de retour : annoter ou inférer
Le type de retour suit la parenthèse fermante : function f(): Type. Il est facultatif. Sans lui, TypeScript infère le type à partir de chaque return du corps.
Une annotation se justifie quand la fonction est exportée, quand elle a plusieurs chemins de retour, ou quand elle est récursive. L'erreur apparaît alors dans la fonction, au mauvais return, et non chez un appelant. Un cas classique est une branche qui oublie de renvoyer une valeur :
Le compilateur signale index.ts(2,32): error TS2366: Function lacks ending return statement and return type does not include 'undefined'. Ajoutez un return "C"; final et le code s'exécute. Sans l'annotation : string, la fonction compilerait avec le type inféré "A" | "B" | undefined, et le problème se déplacerait chez celui qui utilise le résultat.
void : les fonctions qui ne renvoient rien
Une fonction qui se contente d'agir (journaliser, écrire, modifier) a le type de retour void. C'est ce que TypeScript infère quand il n'y a pas de return avec une valeur, et vous pouvez l'écrire explicitement.
Ces règles sur void surprennent :
| Situation | Autorisé ? |
|---|---|
function f(): void { return 42; } | Non : Type 'number' is not assignable to type 'void'. (TS2322) |
const f: () => void = () => 42; | Oui : la valeur est renvoyée, mais les appelants ne doivent pas compter dessus |
function f(): undefined {} | Oui (TypeScript 5.1 et ultérieurs) |
Utiliser le résultat d'une fonction void dans un if | Non : An expression of type 'void' cannot be tested for truthiness. (TS1345) |
La deuxième ligne est voulue. C'est pourquoi arr.forEach(x => list.push(x)) compile alors que push renvoie un nombre. void dans un type de fonction signifie « quoi que cela renvoie, personne ne le lit ». Pour une fonction qui ne se termine jamais (elle lève toujours une exception ou boucle indéfiniment), le type de retour est never.
Fonctions fléchées et expressions de fonction
Les fonctions fléchées et les expressions de fonction prennent les mêmes annotations. Le type de retour se place après la liste des paramètres, avant =>.
Sans les parenthèses, (x, y) => { x, y } est un bloc sans return, et la fonction renvoie undefined. Les règles d'exécution des fonctions fléchées (pas de this propre, pas d'arguments) sont les mêmes qu'en JavaScript ; voir les fonctions fléchées en JavaScript.
Quand une fonction est écrite directement comme argument, vous n'annotez en général pas du tout ses paramètres. TypeScript sait ce que reçoit le callback grâce à la fonction à laquelle il est passé (typage contextuel) : dans [1, 2].map(n => n * 2), n est déjà un number. L'écriture du type d'une fonction en tant que valeur, (n: number) => string, est présentée dans types de fonctions.
Renvoyer plusieurs valeurs
Une fonction renvoie une seule valeur : renvoyez donc un objet ou un tuple, et déstructurez-le.
Le type de retour tuple a besoin de l'annotation. Sans elle, [a, b] est inféré comme string[] : la déstructuration fonctionne toujours, mais le type n'indique plus qu'il y a exactement deux éléments.
Fonctions async
Une fonction async renvoie toujours une Promise. Annotez le type de retour comme Promise<T>, où T est ce que la fonction renvoie à l'intérieur.
async function loadScore(id: number): Promise<number> {
await new Promise((resolve) => setTimeout(resolve, 10));
return id * 10;
}
async function main(): Promise<void> {
const score = await loadScore(4); // score: number
console.log("score", score);
}
main();
Écrire async function f(): number est une erreur : The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<number>'? (TS1064).
Le paramètre this
JavaScript décide de this au moment de l'appel. TypeScript vous permet de déclarer ce que doit être this grâce à un faux premier paramètre nommé this. Il est retiré du JavaScript compilé et les appelants ne le passent pas.
interface Counter {
count: number;
}
function increment(this: Counter, by: number): void {
this.count += by;
}
const c = { count: 0, increment };
c.increment(2); // fine: this is c
increment(2);
// error TS2684: The 'this' context of type 'void' is not assignable to method's 'this' of type 'Counter'.
Sans le paramètre this, this dans une fonction autonome est un any implicite et le mode strict signale 'this' implicitly has type 'any' because it does not have a type annotation. (TS2683). Dans les méthodes de classe, this est déjà typé comme l'instance de la classe : le paramètre sert donc surtout pour les fonctions autonomes rattachées à des objets et pour les callbacks que des bibliothèques appellent avec un this précis.
Questions fréquentes
Comment indiquer le type de retour d'une fonction en TypeScript ?
Écrivez deux-points et le type après la liste des paramètres : function total(a: number, b: number): number { ... }. Pour une fonction fléchée, il se place au même endroit : const total = (a: number, b: number): number => a + b;. Si vous l'omettez, TypeScript infère le type de retour à partir des instructions return.
Faut-il toujours annoter le type de retour en TypeScript ?
C'est facultatif, puisque le type de retour est inféré. Annotez-le sur les fonctions exportées et sur celles qui ont plusieurs chemins de return : l'annotation documente le contrat, et un mauvais return est alors signalé dans la fonction au lieu de l'être chez un appelant lointain.
Quelle est la différence entre void et undefined en TypeScript ?
void signifie « l'appelant ne doit pas utiliser la valeur de retour ». undefined est un type de valeur concret. Une fonction déclarée (): void ne peut pas renvoyer de valeur, mais un type de callback () => void accepte une fonction qui renvoie bel et bien quelque chose, et le résultat est simplement ignoré. Utilisez void pour les fonctions qui ne renvoient rien et undefined seulement quand les appelants comparent réellement le résultat à undefined.
Comment renvoyer plusieurs valeurs depuis une fonction TypeScript ?
Renvoyez un objet ({ min: number; max: number }) ou un tuple ([number, number]) et déstructurez-le à l'appel. Les objets sont plus clairs quand les valeurs ont des sens différents ; les tuples se lisent bien pour de courtes paires comme [value, setValue].
Quel est le type de retour d'une fonction async en TypeScript ?
Toujours une Promise. Une fonction async qui renvoie un number a le type de retour Promise<number>, et y écrire : number est une erreur de compilation (TS1064). Une fonction qui ne renvoie rien a le type Promise<void>.