Menu

Tuplas en TypeScript: sintaxis, nombres, opcionales y rest

Una tupla en TypeScript es un array con un número fijo de elementos cuyos tipos se conocen por posición, como [string, number]. Aprende la sintaxis, los elementos con nombre, opcionales y rest, las tuplas readonly y as const, cómo devolver tuplas desde funciones y en qué se diferencian de los arrays.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Una tupla en TypeScript es un array con un número fijo de elementos, en el que cada posición tiene su propio tipo. [string, number] significa exactamente dos elementos: primero un string y después un número. Escribes los tipos entre corchetes en el orden en que aparecen los valores.

En tiempo de ejecución una tupla es un array normal de JavaScript. Todo lo que añade una tupla (la longitud fija y el tipo de cada posición) lo comprueba el compilador y después se borra.

Sintaxis de las tuplas

Tipo tuplaAceptaTipo de length
[string, number]exactamente un string y luego un número2
[x: number, y: number]lo mismo, con etiquetas para leerlo mejor2
[number, number, number?]2 o 3 números2 | 3
[string, ...number[]]un string y luego cualquier cantidad de númerosnumber
[...string[], number]cualquier cantidad de strings y luego un númeronumber
readonly [number, number]un par que no se puede modificar2
[]solo un array vacío0

Cada forma se explica más abajo. Fíjate en el tipo de length: en una tupla fija es un tipo literal, así que el compilador sabe que pair.length es exactamente 2.

Qué comprueba el compilador

Un tipo tupla fija el número de elementos, su orden y el tipo de cada posición. Equivocarse en cualquiera de ellos es un error de compilación:

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 array normal nunca podría detectar el último: para string[], arr[2] es simplemente un string que en tiempo de ejecución resulta ser undefined.

Elementos con nombre

Las etiquetas documentan qué significa cada posición. No cambian nada del tipo ni de cómo se indexa, pero los editores las muestran al pasar el cursor y en las pistas de firma, lo que hace [number, number] mucho menos misterioso.

Desde TypeScript 5.2 puedes etiquetar unas posiciones y dejar otras sin etiqueta, como en [first: string, number]. Las etiquetas son solo para quien lee: [x: number, y: number] y [number, number] son el mismo tipo y se pueden asignar entre sí.

Elementos opcionales

Un ? después del tipo de un elemento hace opcional esa posición. Los elementos opcionales deben ir después de los obligatorios, y cada uno amplía el tipo de length.

Leer un elemento opcional da T | undefined, así que hace falta un valor por defecto en el patrón de desestructuración (a = 1) o una comprobación antes de hacer aritmética.

Elementos rest

Un elemento rest, ...T[], representa cualquier cantidad de elementos de tipo T. Puede ir al final, al principio o en medio, con uno como máximo por tupla.

El length de una tupla con un elemento rest es number, porque el tamaño ya no es fijo. Lo que sigue fijo es dónde están las posiciones tipadas.

Tuplas readonly y as const

readonly [T, U] elimina push, pop, splice y la asignación por índice, que es lo que debe ser un valor de longitud fija. Escribir as const después de un array literal infiere una tupla readonly de tipos literales.

(typeof SIZES)[number] convierte la tupla en una unión de los tipos de sus elementos, un patrón que se explica en indexed access types. Una tupla readonly no se puede pasar a un parámetro tipado como tupla mutable, así que las funciones que solo leen deberían aceptar readonly [number, number].

La comprobación de readonly es solo en tiempo de compilación. En tiempo de ejecución el array no está congelado (la asignación de arriba se ejecutó de verdad, como muestra la salida), así que usa Object.freeze si necesitas una garantía en tiempo de ejecución.

Devolver una tupla desde una función

Devolver varios valores como tupla es como funciona useState de React (const [value, setValue] = useState(0)). El problema: un array literal en un return se infiere como array, no como tupla.

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 función devuelve (number | (() => number))[], así que los dos nombres desestructurados reciben el tipo unión. Hay dos soluciones: anotar el tipo de retorno o añadir as const.

Devolver una tupla permite a quien llama nombrar las partes como quiera. Cuando hay más de dos o tres valores, o el orden no es evidente, devuelve un objeto: { count, increment } se documenta solo.

Tuplas como parámetros de función

Un parámetro rest tipado como tupla describe una lista de argumentos completa, incluidos los argumentos opcionales. Así es como el tipo utilitario integrado Parameters<T> representa los parámetros de una función.

Expandir una tupla en una llamada comprueba el tipo de cada argumento por posición, algo que no podría hacer la expansión de un (string | number)[].

Tupla frente a array

Array (string | number)[]Tupla [string, number]
Longitudcualquierafija (o acotada por elementos opcionales y rest)
Tipo de x[0]string | numberstring
Tipo de x[5]string | numbererror de compilación TS2493
Tipo de lengthnumber2
Orden de los tiposno se registrase registra
Valor en tiempo de ejecuciónarray de JavaScriptel mismo array de JavaScript
Uso típicolistas de elementos parecidosgrupos pequeños y fijos: pares, coordenadas, [key, value], varios valores de retorno

Las tuplas también aparecen en tipos integrados. Object.entries(obj) devuelve [string, T][], y un Map se construye a partir de tuplas [key, value]:

Una trampa: una tupla mutable sigue teniendo todos los métodos de array, así que pair.push(3) compila sobre un [string, number] y crea sin avisar un array de tres elementos cuyo tipo dice que tiene dos. Declarar las tuplas readonly cierra ese agujero. Y como los tipos se borran, los datos que vienen de fuera del programa (JSON, una API) no se comprueban contra un tipo tupla en tiempo de ejecución: valida su longitud y los tipos de sus elementos antes de fiarte de ellos.

Tuplas variádicas

Los tipos tupla pueden expandir otros tipos tupla, [...T, ...U]. Junto con los genéricos, esto permite tipar funciones que concatenan o anteponen elementos conservando cada posición:

Los tipos de las librerías también se apoyan en la inferencia de tuplas: Promise.all([fetchUser(), fetchPosts()]) se resuelve en una tupla con un tipo por cada promesa de entrada.

Preguntas frecuentes

¿Qué es una tupla en TypeScript?

Una tupla es un tipo array de longitud fija en el que cada posición tiene su propio tipo: [string, number] son exactamente dos elementos, un string y luego un número. En tiempo de ejecución es un array normal de JavaScript; la longitud y los tipos por posición solo se comprueban en tiempo de compilación.

¿Qué diferencia hay entre una tupla y un array en TypeScript?

Un tipo array como (string | number)[] tiene cualquier longitud y todos sus elementos tienen el mismo tipo (la unión), así que arr[0] es string | number. Una tupla como [string, number] tiene una longitud conocida, y t[0] es string, t[1] es number y t[2] es un error de compilación.

¿Cómo devuelvo una tupla desde una función en TypeScript?

Anota el tipo de retorno, function f(): [number, string], o termina la expresión del return con as const, que da una tupla readonly. Sin ninguna de las dos, return [count, setCount] se infiere como un array de una unión, como (number | (() => void))[], y al desestructurar obtienes tipos unión.

¿Qué son los elementos con nombre de una tupla?

Etiquetas en las posiciones, [name: string, age: number]. No cambian el tipo ni la forma de acceder (sigue siendo t[0]), pero los editores las muestran al pasar el cursor y en las pistas de parámetros de funciones cuyos parámetros se tipan como tupla. Los elementos opcionales y rest funcionan con etiquetas: [x: number, y?: number], [head: string, ...rest: number[]].

¿Se puede hacer push a una tupla en TypeScript?

En una tupla mutable, sí: push compila porque las tuplas heredan los métodos de array, aunque rompa la longitud fija. Declara la tupla readonly (o créala con as const) y push, pop y la asignación por índice pasan a ser errores de compilación.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR