Menu

Tuple en TypeScript : syntaxe, éléments nommés, optionnels et rest

Un tuple TypeScript est un tableau au nombre d'éléments fixe dont les types sont connus position par position, comme [string, number]. Découvrez la syntaxe, les éléments nommés, optionnels et rest, les tuples readonly et as const, le retour d'un tuple depuis une fonction, et la différence avec les tableaux.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un tuple TypeScript est un tableau au nombre d'éléments fixe, où chaque position a son propre type. [string, number] signifie exactement deux éléments : d'abord une chaîne, puis un nombre. Vous écrivez les types entre crochets, dans l'ordre où les valeurs apparaissent.

À l'exécution, un tuple est un simple tableau JavaScript. Tout ce qu'apporte un tuple (la longueur fixe et le type à chaque position) est vérifié par le compilateur, puis effacé.

Syntaxe des tuples

Type tupleAccepteType de length
[string, number]exactement une chaîne, puis un nombre2
[x: number, y: number]la même chose, avec des étiquettes pour la lisibilité2
[number, number, number?]2 ou 3 nombres2 | 3
[string, ...number[]]une chaîne, puis un nombre quelconque de nombresnumber
[...string[], number]un nombre quelconque de chaînes, puis un nombrenumber
readonly [number, number]une paire non modifiable2
[]uniquement un tableau vide0

Chaque forme est expliquée plus bas. Le type de length mérite l'attention : pour un tuple fixe, c'est un type littéral, le compilateur sait donc que pair.length vaut exactement 2.

Ce que vérifie le compilateur

Un type tuple fixe le nombre d'éléments, leur ordre et le type à chaque position. Se tromper sur l'un d'eux est une erreur de compilation :

index.ts(2,7): error TS2322: Type '[string]' is not assignable to type '[string, number]'.
  Source has 1 element(s) but target requires 2.
index.ts(3,36): error TS2322: Type 'number' is not assignable to type 'string'.
index.ts(3,40): error TS2322: Type 'string' is not assignable to type 'number'.
index.ts(5,16): error TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.

Un simple tableau ne pourrait jamais détecter le dernier cas : pour string[], arr[2] est simplement un string qui se trouve valoir undefined à l'exécution.

Éléments nommés

Les étiquettes documentent le sens de chaque position. Elles ne changent rien au type ni à l'accès par indice, mais les éditeurs les affichent au survol et dans les indications de signature, ce qui rend [number, number] bien moins mystérieux.

Depuis TypeScript 5.2, vous pouvez étiqueter certaines positions et pas d'autres, comme dans [first: string, number]. Les étiquettes ne servent qu'aux lecteurs : [x: number, y: number] et [number, number] sont le même type et assignables l'un à l'autre.

Éléments optionnels

Un ? après le type d'un élément rend cette position optionnelle. Les éléments optionnels doivent venir après les éléments obligatoires, et chacun élargit le type de length.

Lire un élément optionnel donne T | undefined : une valeur par défaut dans le motif de déstructuration (a = 1) ou une vérification est nécessaire avant tout calcul.

Éléments rest

Un élément rest, ...T[], représente un nombre quelconque d'éléments de type T. Il peut se trouver à la fin, au début ou au milieu, avec au plus un par tuple.

La length d'un tuple avec un élément rest est number, puisque la taille n'est plus fixe. Ce qui reste fixe, c'est l'emplacement des positions typées.

Tuples readonly et as const

readonly [T, U] retire push, pop, splice et l'assignation par indice, ce qui correspond bien à une valeur de longueur fixe. Écrire as const après un littéral de tableau infère un tuple readonly de types littéraux.

(typeof SIZES)[number] transforme le tuple en union des types de ses éléments, un motif présenté sur la page des types d'accès indexé. Un tuple readonly ne peut pas être passé à un paramètre typé comme tuple modifiable, les fonctions qui se contentent de lire devraient donc accepter readonly [number, number].

La vérification readonly n'a lieu qu'à la compilation. À l'exécution, le tableau n'est pas gelé (l'assignation ci-dessus a bien eu lieu, comme le montre la sortie) : utilisez Object.freeze si vous avez besoin d'une garantie à l'exécution.

Renvoyer un tuple depuis une fonction

Renvoyer plusieurs valeurs sous forme de tuple, c'est le fonctionnement du useState de React (const [value, setValue] = useState(0)). Le piège : un littéral de tableau dans un return est inféré comme un tableau, pas comme un tuple.

index.ts(9,13): error TS2365: Operator '+' cannot be applied to types 'number | (() => number)' and 'number'.
index.ts(10,1): error TS2349: This expression is not callable.
  Not all constituents of type 'number | (() => number)' are callable.
    Type 'number' has no call signatures.

La fonction renvoie (number | (() => number))[], donc les deux noms déstructurés reçoivent le type union. Il y a deux corrections : annoter le type de retour, ou ajouter as const.

Une valeur de retour en tuple laisse les appelants nommer les parties comme ils le veulent. Quand il y a plus de deux ou trois valeurs, ou que l'ordre n'est pas évident, renvoyez plutôt un objet : { count, increment } se documente tout seul.

Les tuples comme paramètres de fonction

Un paramètre rest typé par un tuple décrit toute une liste d'arguments, arguments optionnels compris. C'est ainsi que le type utilitaire intégré Parameters<T> représente les paramètres d'une fonction.

Étaler un tuple dans un appel vérifie chaque argument selon sa position, ce qu'un étalement de (string | number)[] ne pourrait pas faire.

Tuple ou tableau

Tableau (string | number)[]Tuple [string, number]
Longueurquelconquefixe (ou bornée par les éléments optionnels et rest)
Type de x[0]string | numberstring
Type de x[5]string | numbererreur de compilation TS2493
Type de lengthnumber2
Ordre des typesnon suivisuivi
Valeur à l'exécutiontableau JavaScriptle même tableau JavaScript
Usage typiquelistes d'éléments similairespetits groupes fixes : paires, coordonnées, [key, value], valeurs de retour multiples

Les tuples apparaissent aussi dans les types intégrés. Object.entries(obj) renvoie [string, T][], et une Map se construit à partir de tuples [key, value] :

Un piège : un tuple modifiable possède toujours toutes les méthodes de tableau, donc pair.push(3) compile sur un [string, number] et crée discrètement un tableau de trois éléments dont le type en annonce deux. Déclarer les tuples readonly ferme cette brèche. Et comme les types sont effacés, les données venues de l'extérieur du programme (JSON, une API) ne sont pas vérifiées par rapport à un type tuple à l'exécution : validez leur longueur et le type de leurs éléments avant de leur faire confiance.

Types tuple variadiques

Les types tuple peuvent étaler d'autres types tuple, [...T, ...U]. Combiné aux génériques, cela permet de typer des fonctions qui concatènent ou ajoutent en tête tout en conservant chaque position :

Les types des bibliothèques s'appuient eux aussi sur l'inférence de tuples : Promise.all([fetchUser(), fetchPosts()]) se résout en un tuple avec un type par promesse en entrée.

Questions fréquentes

Qu'est-ce qu'un tuple en TypeScript ?

Un tuple est un type tableau de longueur fixe où chaque position a son propre type : [string, number] désigne exactement deux éléments, une chaîne puis un nombre. À l'exécution, c'est un tableau JavaScript ordinaire ; la longueur et les types par position ne sont vérifiés qu'à la compilation.

Quelle est la différence entre un tuple et un tableau en TypeScript ?

Un type tableau comme (string | number)[] a une longueur quelconque et tous ses éléments ont le même type (union), donc arr[0] est string | number. Un tuple comme [string, number] a une longueur connue, t[0] est string, t[1] est number, et t[2] est une erreur de compilation.

Comment renvoyer un tuple depuis une fonction en TypeScript ?

Annotez le type de retour, function f(): [number, string], ou terminez l'expression renvoyée par as const, ce qui donne un tuple readonly. Sans l'un ou l'autre, return [count, setCount] est inféré comme un tableau d'union, du genre (number | (() => void))[], et la déstructuration donne des types union.

Que sont les éléments nommés d'un tuple ?

Des étiquettes sur les positions, [name: string, age: number]. Elles ne changent ni le type ni la façon d'y accéder (toujours t[0]), mais les éditeurs les affichent au survol et dans les indications de paramètres des fonctions dont les paramètres sont typés par un tuple. Les éléments optionnels et rest fonctionnent avec les étiquettes : [x: number, y?: number], [head: string, ...rest: number[]].

Peut-on faire un push sur un tuple en TypeScript ?

Sur un tuple modifiable, oui : push compile, car les tuples héritent des méthodes de tableau, même si cela casse la longueur fixe. Déclarez le tuple readonly (ou créez-le avec as const) et push, pop ainsi que l'assignation par indice deviennent des erreurs de compilation.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER