Moins de code, plus d'utilité : décortiquons les fonctions fléchées et les génériques en TypeScript
Avez-vous déjà écrit une petite utilitaire en TypeScript qui devait fonctionner à la fois avec des nombres, des chaînes de caractères et des tableaux ? Et vous êtes immédiatement heurté à un problème : comment rendre le type de données flexible tout en évitant que TypeScript ne râle ? La réponse : les génériques (generics, ou types génériques). Et si vous aimez la syntaxe concise, vous utilisez sûrement les fonctions fléchées.
Mais voilà le hic : sur Stack Overflow, la question « What is the syntax for Typescript arrow functions with generics? » a accumulé des milliers de vues. Il s'avère que même les développeurs expérimentés s'y perdent parfois dans cette combinaison pourtant simple. Dans cet article, nous allons voir comment écrire correctement des TypeScript arrow functions avec des génériques, examiner les erreurs fréquentes et montrer comment rendre votre code plus propre et plus sûr.
Prêt ? C'est parti !
Que sont les génériques dans les fonctions fléchées ?
Pour commencer — une brève introduction. Les génériques permettent de créer des composants (fonctions, classes, interfaces) qui fonctionnent avec différents types, plutôt qu'avec un seul type spécifique. Au lieu d'écrire des fonctions séparées pour les nombres et les chaînes, vous en écrivez une seule — avec un générique.
Les fonctions fléchées (arrow functions) sont une syntaxe compacte pour les fonctions en JavaScript et TypeScript. Lorsque vous les combinez avec des génériques, vous obtenez un outil puissant pour créer des utilitaires réutilisables.
// Fonction classique avec un génériquefunction identity<T>(arg: T): T { return arg;}
// Fonction fléchée avec un générique — à quoi cela ressemble ?const arrowIdentity = <T>(arg: T): T => arg;Tout simple ? Oui, mais il y a des nuances que nous allons détailler ci-dessous.
Syntaxe de base : comment déclarer correctement une TypeScript arrow function avec generics
La manière la plus simple et la plus courante est de spécifier le paramètre de type (générique) avant les parenthèses contenant les arguments :
const firstElement = <T>(arr: T[]): T | undefined => { return arr[0];};
// Utilisationconst num = firstElement([1, 2, 3]); // type : number | undefinedconst str = firstElement(['a', 'b']); // type : string | undefinedIci, <T> est la déclaration du générique. TypeScript déduira automatiquement le type T en fonction de l'argument passé. Si vous souhaitez spécifier explicitement le type, cela fonctionne aussi :
const num = firstElement<number>([1, 2, 3]); // On spécifie explicitement le typeImportant : En JSX (React), cette syntaxe peut entrer en conflit avec le balisage. Il existe des solutions de contournement pour ces cas (voir ci-dessous).
Plusieurs génériques dans une même fonction fléchée
Vous pouvez utiliser plusieurs paramètres de type :
const pair = <T, U>(first: T, second: U): [T, U] => { return [first, second];};
const result = pair(42, 'hello'); // type : [number, string]Cela est particulièrement utile lorsque vous travaillez avec des utilitaires comme map, reduce ou lors de la création de wrappers.
Problèmes avec JSX (React) et leurs solutions
Si vous écrivez en React avec TypeScript, vous remarquerez que la syntaxe <T>(arg) peut être interprétée par le compilateur comme une balise JSX. C'est un problème classique, qui explique pourquoi la question sur Stack Overflow a rencontré un tel succès.
Il existe deux solutions principales :
1. Utiliser extends (même si ce n'est pas nécessaire)
Ajoutez extends unknown ou extends Record<string, never> :
const myFunction = <T extends unknown>(arg: T): T => { return arg;};Cela indique explicitement à TypeScript que <T> est un générique, et non du JSX.
2. Utiliser la syntaxe avec une virgule
Dans certains cas, l'ajout d'une virgule après le générique aide :
const myFunction = <T,>(arg: T):