Definição Fundamental e Comparação com Arrays
No TypeScript, uma tupla (tuple) é uma estrutura de dados que difere de um array padrão por permitir que seus elementos tenham tipos distintos, desde que definidos explicitamente. A declaração de tipo ocorre dentro dos colchetes.
| Aspecto | Tupla | Array |
|---|---|---|
| Tipos dos elementos | Podem ser dfierentes (ex.: [string, boolean]) |
Deve ser homogêneo (ex.: number[]) |
| Sintaxe de Declaração | [T1, T2, Tn] |
T[] ou Array<T> |
| Inferência de tipo | Sem declaração explícita, torna-se um array de união de tipos. | Inferido automaticamente como array do tipo único. |
Exemplos de código ilustrando a necessidade de tipagem explícita:
// Correto: tupla com tipos definidos
const coordenada: [number, number] = [10, 20];
// Incorreto: sem declaração explícita, é inferido como (string | number)[]
const dados = ["Alice", 25]; // Não é uma tupla.
Membros Opcionais e o Operador Spread
Uma tupla pode conter membros opcionais, denotados por um ? após o tipo. Estes devem obrigatoriamente estar posicionados no final da tupla.
// Membro 'y' é opcional
type CoordenadaOpcional = [number, number?];
const ponto1: CoordenadaOpcional = [5];
const ponto2: CoordenadaOpcional = [5, 10];
O operador spread (...) permite definir seções com um número variável de elementos. A posição é flexível, mas deve ser seguida por um tipo array ou tupla.
// Tupla com um nome seguido de qualquer quantidade de números
type Registro = [string, ...number[]];
const reg: Registro = ["Sensor", 10.2, 11.5, 9.8];
Tuplas Somente Leitura
Para criar uma tupla imutável, existem duas sintaexs equivalentes: a palavra-chave readonly ou o utility type Readonly.
type PontoFixo1 = readonly [number, number];
type PontoFixo2 = Readonly<[number, number]>;
const pFixo: PontoFixo1 = [1, 2];
// pFixo[0] = 3; // Erro: não é possível atribuir a uma propriedade 'readonly'.
Existe uma relação de subtipo entre tuplas: uma tupla comum pode ser atribuída a uma variável de tipo tupla somente leitura, mas o contrário não é permitido sem uma asserção de tipo.
const tuplaComum: [string, string] = ["a", "b"];
const tuplaLeitura: readonly [string, string] = tuplaComum; // Permitido.
// tuplaComum = tuplaLeitura; // Erro.
Inferência do Comprimento
O TypeScript infere a "largura" da tupla com base na sua definição:
- Comprimanto fixo: Nenhum membro opcional ou spread. O acesso a um índice fora do limite gera um erro.
- Comprimento variável (mínimo): Presença de membros opcionais. O comprimento é uma união de números possíveis.
- Comprimento indefinido: Presença de um operador spread. A tupla é tratada como um array para fins de verificação de comprimento.
Resolução de Erros com Funções de Aritidade Fixa
Passar um array comum para uma função que espera um número fixo de parâmetros usando o spread resulta em um erro, pois o TypeScript não pode garantir o número de elementos.
const valores = [1, 2]; // Inferido como number[]
function somar(a: number, b: number): number { return a + b; }
// somar(...valores); // Erro: argumento do tipo 'number[]' não é atribuível.
Solução 1: Tipar explicitamente a variável como uma tupla de tamanho fixo.
const par: [number, number] = [1, 2];
somar(...par); // Correto.
Solução 2: Usar a asserção as const, que transforma a estrutura em uma tupla somente leitura com tipos literais.
const parConstante = [1, 2] as const; // Inferido como readonly [1, 2]
somar(...parConstante); // Correto.