Arrays e Tuplas Tipados
1. Fundamentos de Arrays Tipados
Em TypeScript, arrays tipados são a base para trabalhar com coleções de dados de forma segura. Existem duas sintaxes equivalentes para declarar arrays tipados:
// Sintaxe com colchetes (mais comum)
let numeros: number[] = [1, 2, 3, 4, 5];
// Sintaxe genérica
let nomes: Array<string> = ["Alice", "Bob", "Charlie"];
A inferência de tipos funciona de maneira inteligente com arrays literais:
// TypeScript infere como number[]
const valores = [10, 20, 30];
// Infere como (string | number)[]
const misto = ["texto", 42, "mais texto", 100];
Arrays vazios merecem atenção especial. Inicialmente, o TypeScript infere o tipo never[], que não permite adicionar elementos:
let arrayVazio = []; // tipo: never[]
// arrayVazio.push(1); // Erro! Argumento do tipo 'number' não é atribuível ao parâmetro do tipo 'never'
Para corrigir isso, devemos anotar o tipo explicitamente:
let arrayVazio: number[] = [];
arrayVazio.push(1); // Agora funciona
2. Operações e Métodos com Arrays Tipados
Os métodos nativos de array preservam a segurança de tipos:
const numeros: number[] = [1, 2, 3, 4, 5];
// push aceita apenas números
numeros.push(6);
// numeros.push("texto"); // Erro!
// map preserva o tipo do retorno
const dobrados: number[] = numeros.map(n => n * 2);
// filter também mantém a segurança
const pares: number[] = numeros.filter(n => n % 2 === 0);
Arrays somente leitura oferecem proteção contra mutações:
// Duas formas equivalentes
const frutas1: readonly string[] = ["maçã", "banana"];
const frutas2: ReadonlyArray<string> = ["maçã", "banana"];
// frutas1.push("laranja"); // Erro! Propriedade 'push' não existe em 'readonly string[]'
// frutas1[0] = "pêra"; // Erro! Índice somente leitura
// Métodos que não mutam ainda funcionam
const frutasMaiusculas = frutas1.map(f => f.toUpperCase());
É importante distinguir métodos que mutam o array original dos que retornam novos arrays:
const original: number[] = [1, 2, 3];
// Mutam o original
original.push(4); // original agora é [1, 2, 3, 4]
original.pop(); // original agora é [1, 2, 3]
original.sort(); // ordena in-place
// Retornam novo array (não mutam)
const copia = original.concat([4, 5]); // [1, 2, 3, 4, 5]
const mapeado = original.map(n => n * 2); // [2, 4, 6]
const filtrado = original.filter(n => n > 1); // [2, 3]
3. Tuplas: Estruturas de Tamanho Fixo
Tuplas representam arrays com número fixo de elementos, onde cada posição pode ter um tipo diferente:
// Tupla básica: [string, number]
let pessoa: [string, number] = ["Alice", 30];
// Acesso com segurança de tipos
const nome: string = pessoa[0]; // "Alice"
const idade: number = pessoa[1]; // 30
// pessoa[2] = "extra"; // Erro! Tipo 'string' não pode ser atribuído a 'undefined'
A inferência de tipos em tuplas literais requer atenção:
// TypeScript infere como (string | number)[]
const tuplaInferida = ["texto", 42];
// Para forçar tupla, use 'as const'
const tuplaExplicita = ["texto", 42] as const;
// Tipo: readonly ["texto", 42]
4. Tuplas com Elementos Opcionais e Rest
Elementos opcionais em tuplas são úteis para representar dados que podem ou não estar presentes:
// Elemento opcional no final
type Config = [string, number?];
const config1: Config = ["produção"];
const config2: Config = ["desenvolvimento", 3000];
// const config3: Config = ["teste", 3000, "extra"]; // Erro!
Elementos rest permitem tuplas com número variável de elementos:
// Tupla com um ou mais números
type PeloMenosUmNumero = [number, ...number[]];
const lista1: PeloMenosUmNumero = [1];
const lista2: PeloMenosUmNumero = [1, 2, 3, 4, 5];
// Caso de uso: função com parâmetros variádicos
function somar(...numeros: [number, ...number[]]): number {
return numeros.reduce((acc, curr) => acc + curr, 0);
}
console.log(somar(1)); // 1
console.log(somar(1, 2, 3)); // 6
5. Manipulação e Desestruturação de Tuplas
A desestruturação preserva os tipos individuais dos elementos:
type Coordenada = [number, number, string];
const ponto: Coordenada = [10, 20, "Ponto A"];
// Desestruturação com tipos preservados
const [x, y, nome] = ponto;
// x: number, y: number, nome: string
console.log(`Coordenadas de ${nome}: (${x}, ${y})`);
Labeled tuples tornam o código mais legível:
type Usuario = [nome: string, idade: number, ativo: boolean];
function criarUsuario(...args: Usuario): void {
const [nome, idade, ativo] = args;
console.log(`${nome} tem ${idade} anos e está ${ativo ? "ativo" : "inativo"}`);
}
criarUsuario("Alice", 30, true);
Spread de tuplas em parâmetros de função:
function logInfo(nome: string, idade: number, cidade: string): void {
console.log(`${nome}, ${idade} anos, mora em ${cidade}`);
}
const dados: [string, number, string] = ["Bob", 25, "São Paulo"];
logInfo(...dados); // Spread da tupla
6. Tipos Avançados com Arrays e Tuplas
Union types em arrays permitem múltiplos tipos:
const misturado: (string | number | boolean)[] = [true, "texto", 42, false];
// Útil para dados heterogêneos
type Flexivel = (string | number)[];
const dados: Flexivel = ["código", 123, "nome", 456];
Arrays de objetos tipados são essenciais em aplicações reais:
interface Usuario {
id: number;
nome: string;
email: string;
}
const usuarios: Usuario[] = [
{ id: 1, nome: "Alice", email: "alice@email.com" },
{ id: 2, nome: "Bob", email: "bob@email.com" }
];
// Operações seguras com objetos
const emails: string[] = usuarios.map(u => u.email);
const usuarioPorId = usuarios.find(u => u.id === 1);
Mapped types aplicados a arrays:
type Opcional<T> = { [K in keyof T]?: T[K] };
type UsuarioOpcional = Opcional<Usuario>;
// { id?: number; nome?: string; email?: string; }
// Aplicado a arrays
type ArrayOpcional<T> = Opcional<T>[];
const usuariosParciais: ArrayOpcional<Usuario> = [
{ nome: "Alice" },
{ id: 2, email: "bob@email.com" }
];
7. Boas Práticas e Armadilhas Comuns
Evite any[] a todo custo, pois ele desativa a verificação de tipos:
// Ruim
const dadosRuins: any[] = [1, "texto", { chave: "valor" }];
// Bom
type DadosBons = (number | string | { chave: string })[];
const dadosBons: DadosBons = [1, "texto", { chave: "valor" }];
Entenda a diferença fundamental entre tuplas e arrays com union types:
// Tupla: posições específicas com tipos específicos
type TuplaExemplo = [string, number];
const tupla: TuplaExemplo = ["Alice", 30]; // Correto
// const tuplaErrada: TuplaExemplo = [30, "Alice"]; // Erro!
// Array com union: qualquer posição aceita qualquer tipo
type ArrayExemplo = (string | number)[];
const array: ArrayExemplo = ["Alice", 30]; // Correto
const array2: ArrayExemplo = [30, "Alice"]; // Também correto!
Quando usar array vs tupla:
// Use array quando:
// - Número de elementos é variável
// - Todos os elementos têm o mesmo tipo
const listaDeCompras: string[] = ["arroz", "feijão", "carne"];
// Use tupla quando:
// - Número de elementos é fixo e conhecido
// - Cada posição tem um significado específico
type CoordenadasGPS = [latitude: number, longitude: number, altitude: number];
const localizacao: CoordenadasGPS = [-23.5505, -46.6333, 760];
Referências
- TypeScript Handbook: Array Types — Documentação oficial sobre arrays tipados em TypeScript
- TypeScript Handbook: Tuple Types — Guia completo sobre tuplas na documentação oficial
- TypeScript Deep Dive: TypeScript Arrays — Explicação detalhada sobre arrays e inferência de tipos
- TypeScript Evolution: Variadic Tuple Types — Anúncio oficial dos tipos de tupla variádicos no TypeScript 4.0
- TypeScript Playground: Tuples Examples — Exemplos interativos de tuplas no playground oficial
- Total TypeScript: Tuples in TypeScript — Tutorial prático sobre tuplas com exemplos avançados
- TypeScript 4.0: Labeled Tuple Elements — Documentação sobre elementos de tupla rotulados introduzidos no TypeScript 4.0