Menu

Tuple in TypeScript: sintassi, elementi con nome, opzionali e rest

Una tuple in TypeScript è un array con un numero fisso di elementi il cui tipo è noto per posizione, come [string, number]. Scopri la sintassi, gli elementi con nome, opzionali e rest, le tuple readonly e as const, come restituire tuple dalle funzioni e in cosa differiscono dagli array.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Una tuple in TypeScript è un array con un numero fisso di elementi, in cui ogni posizione ha il proprio tipo. [string, number] significa esattamente due elementi: prima una stringa, poi un numero. Scrivi i tipi tra parentesi quadre nell'ordine in cui compaiono i valori.

A runtime una tuple è un semplice array JavaScript. Tutto ciò che una tuple aggiunge (la lunghezza fissa e il tipo in ogni posizione) viene controllato dal compilatore e poi eliminato.

Sintassi delle tuple

Tipo tupleAccettaTipo di length
[string, number]esattamente una stringa, poi un numero2
[x: number, y: number]lo stesso, con etichette per la leggibilità2
[number, number, number?]2 o 3 numeri2 | 3
[string, ...number[]]una stringa, poi un numero qualsiasi di numerinumber
[...string[], number]un numero qualsiasi di stringhe, poi un numeronumber
readonly [number, number]una coppia che non si può modificare2
[]solo un array vuoto0

Ogni forma è spiegata più avanti. Vale la pena notare il tipo di length: per una tuple fissa è un tipo letterale, quindi il compilatore sa che pair.length è esattamente 2.

Cosa controlla il compilatore

Un tipo tuple fissa il numero di elementi, il loro ordine e il tipo in ogni posizione. Sbagliarne uno qualsiasi è un errore di compilazione:

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 semplice array non potrebbe mai intercettare l'ultimo: per string[], arr[2] è semplicemente una string che a runtime si rivela undefined.

Elementi con nome

Le etichette documentano il significato di ogni posizione. Non cambiano nulla del tipo né del modo di indicizzarlo, ma gli editor le mostrano al passaggio del mouse e nei suggerimenti della firma, e così [number, number] diventa molto meno misterioso.

Da TypeScript 5.2 puoi etichettare alcune posizioni e lasciarne altre senza etichetta, come in [first: string, number]. Le etichette servono solo a chi legge: [x: number, y: number] e [number, number] sono lo stesso tipo e assegnabili l'uno all'altro.

Elementi opzionali

Un ? dopo il tipo di un elemento rende opzionale quella posizione. Gli elementi opzionali devono venire dopo quelli obbligatori, e ognuno allarga il tipo di length.

Leggere un elemento opzionale dà T | undefined, quindi serve un valore predefinito nel pattern di destrutturazione (a = 1) o un controllo prima di fare calcoli.

Elementi rest

Un elemento rest, ...T[], rappresenta un numero qualsiasi di elementi di tipo T. Può stare alla fine, all'inizio o in mezzo, al massimo uno per tuple.

La length di una tuple con un elemento rest è number, perché la dimensione non è più fissa. Ciò che resta fisso è la posizione degli elementi tipizzati.

Tuple readonly e as const

readonly [T, U] toglie push, pop, splice e l'assegnazione tramite indice, cioè ciò che un valore di lunghezza fissa dovrebbe essere. Scrivere as const dopo un array letterale deduce una tuple readonly di tipi letterali.

(typeof SIZES)[number] trasforma la tuple in una union dei tipi dei suoi elementi, un pattern trattato negli indexed access types. Una tuple readonly non si può passare a un parametro tipizzato come tuple modificabile, quindi le funzioni che si limitano a leggere dovrebbero accettare readonly [number, number].

Il controllo readonly esiste solo in fase di compilazione. A runtime l'array non è congelato (l'assegnazione qui sopra è stata davvero eseguita, come mostra l'output), quindi usa Object.freeze se ti serve una garanzia a runtime.

Restituire una tuple da una funzione

Restituire più valori come tuple è il modo in cui funziona useState di React (const [value, setValue] = useState(0)). Il problema: un array letterale in un return viene dedotto come array, non come 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 funzione restituisce (number | (() => number))[], quindi entrambi i nomi destrutturati ricevono il tipo union. Ci sono due soluzioni: annotare il tipo di ritorno, oppure aggiungere as const.

Un valore di ritorno tuple permette a chi chiama la funzione di dare alle parti il nome che preferisce. Quando i valori sono più di due o tre, o l'ordine non è ovvio, restituisci un oggetto: { count, increment } si documenta da solo.

Tuple come parametri di funzione

Un parametro rest tipizzato come tuple descrive un'intera lista di argomenti, compresi quelli opzionali. È così che l'utility type integrato Parameters<T> rappresenta i parametri di una funzione.

Lo spread di una tuple in una chiamata controlla ogni argomento per posizione, cosa che lo spread di un (string | number)[] non potrebbe fare.

Tuple e array a confronto

Array (string | number)[]Tuple [string, number]
Lunghezzaqualsiasifissa (o limitata da elementi opzionali e rest)
Tipo di x[0]string | numberstring
Tipo di x[5]string | numbererrore di compilazione TS2493
Tipo di lengthnumber2
Ordine dei tipinon tracciatotracciato
Valore a runtimearray JavaScriptlo stesso array JavaScript
Uso tipicoliste di elementi similipiccoli gruppi fissi: coppie, coordinate, [key, value], più valori di ritorno

Le tuple compaiono anche nei tipi integrati. Object.entries(obj) restituisce [string, T][], e una Map si costruisce da tuple [key, value]:

Una trappola: una tuple modificabile ha comunque tutti i metodi degli array, quindi pair.push(3) compila su una [string, number] e crea in silenzio un array di tre elementi il cui tipo ne dichiara due. Dichiarare le tuple readonly chiude questa falla. E poiché i tipi vengono eliminati, i dati che arrivano da fuori dal programma (JSON, un'API) non vengono controllati rispetto a un tipo tuple a runtime: verifica la lunghezza e i tipi degli elementi prima di fidarti.

Variadic tuple types

I tipi tuple possono fare lo spread di altri tipi tuple, [...T, ...U]. Insieme ai generics, questo permette di tipizzare funzioni che concatenano o antepongono elementi mantenendo ogni posizione:

Anche i tipi delle librerie si appoggiano all'inferenza delle tuple: Promise.all([fetchUser(), fetchPosts()]) si risolve in una tuple con un tipo per ogni promise in ingresso.

Domande frequenti

Cos'è una tuple in TypeScript?

Una tuple è un tipo array di lunghezza fissa in cui ogni posizione ha il proprio tipo: [string, number] sono esattamente due elementi, prima una stringa e poi un numero. A runtime è un normale array JavaScript; la lunghezza e i tipi per posizione vengono controllati solo in fase di compilazione.

Che differenza c'è tra una tuple e un array in TypeScript?

Un tipo array come (string | number)[] ha una lunghezza qualsiasi e ogni elemento ha lo stesso tipo (union), quindi arr[0] è string | number. Una tuple come [string, number] ha una lunghezza nota: t[0] è string, t[1] è number e t[2] è un errore di compilazione.

Come restituisco una tuple da una funzione in TypeScript?

Annota il tipo di ritorno, function f(): [number, string], oppure chiudi l'espressione di ritorno con as const, che produce una tuple readonly. Senza nessuna delle due, return [count, setCount] viene dedotto come array di una union, per esempio (number | (() => void))[], e la destrutturazione dà tipi union.

Cosa sono gli elementi con nome di una tuple?

Etichette sulle posizioni, [name: string, age: number]. Non cambiano il tipo né il modo di accedervi (sempre t[0]), ma gli editor le mostrano al passaggio del mouse e nei suggerimenti dei parametri delle funzioni i cui parametri sono tipizzati come tuple. Gli elementi opzionali e rest funzionano con le etichette: [x: number, y?: number], [head: string, ...rest: number[]].

Si può fare push su una tuple in TypeScript?

Su una tuple modificabile sì: push compila, perché le tuple ereditano i metodi degli array, anche se rompe la lunghezza fissa. Dichiara la tuple readonly (o creala con as const) e push, pop e l'assegnazione tramite indice diventano errori di compilazione.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA