Record em TypeScript: entendendo na prática
\n\nImagine que você está escrevendo um aplicativo para aprender palavras em inglês. Você precisa armazenar traduções: cat → gato, dog → cachorro. Como tipar isso corretamente em TypeScript? Você pode criar uma interface, mas e se as chaves não forem conhecidas antecipadamente? Ou se houver muitas delas? É aí que entra o Record — um dos tipos utilitários mais úteis, mas frequentemente subestimados.
Neste artigo, vamos explorar o que é o Record type em TypeScript, com exemplos reais que você pode aplicar imediatamente em seus projetos. Nada de teoria chata — apenas prática.
\n\nO que é Record em TypeScript? Em palavras simples
\n\nRecord<K, V> é um tipo utilitário embutido que cria um objeto onde todas as chaves são do tipo K e todos os valores são do tipo V.
Pense nele como um dicionário tipado ou um mapa com um conjunto fixo de chaves.
A sintaxe básica é assim:
\n\n// Objeto comum\nconst userRoles: Record<string, string> = {\n admin: 'Administrador',\n user: 'Usuário',\n guest: 'Convidado'\n};\n\nAqui, Record<string, string> significa: "Um objeto onde as chaves são strings e os valores também são strings". Simples e direto.
Por que Record é melhor que uma interface comum?
\n\n- \n
- Flexibilidade de chaves: Você pode definir um conjunto específico de chaves usando um tipo union. \n
- Concisão: Não precisa descrever cada campo manualmente. \n
- Segurança: O TypeScript verifica se você está usando apenas chaves permitidas. \n
Record vs objeto simples: exemplo prático
\n\nSuponha que temos status de pedido: pending, shipped, delivered. Sem Record, você teria que escrever uma interface com três campos:
interface OrderStatusMap {\n pending: string;\n shipped: string;\n delivered: string;\n}\n\nconst statusDescriptions: OrderStatusMap = {\n pending: 'Aguardando processamento',\n shipped: 'Enviado',\n delivered: 'Entregue'\n};\n\nCom Record, fica mais compacto e claro:
\n\ntype OrderStatus = 'pending' | 'shipped' | 'delivered';\n\nconst statusDescriptions: Record<OrderStatus, string> = {\n pending: 'Aguardando processamento',\n shipped: 'Enviado',\n delivered: 'Entregue'\n};\n\nAlém disso: se você tentar adicionar cancelled ao objeto, o TypeScript apontará imediatamente o erro — esse status não existe no tipo union.
Top 5 exemplos práticos de uso do Record
\n\nVamos ver onde o Record realmente simplifica a vida.
\n\n1. Configuração de aplicativo
\nArmazene configurações com chaves conhecidas, mas tipos de valores diferentes:
\n\ntype ConfigKeys = 'apiUrl' | 'timeout' | 'retryCount';\ntype ConfigValue = string | number | boolean;\n\nconst appConfig: Record<ConfigKeys, ConfigValue> = {\n apiUrl: 'https://api.example.com',\n timeout: 5000,\n retryCount: 3\n};\n\n2. Mapeamento de enum ou constantes
\nFrequentemente é necessário mapear um código e seu nome legível por humanos:
\n\nenum HttpStatus {\n OK = 200,\n NotFound = 404,\n Error = 500\n}\n\nconst statusMessages: Record<HttpStatus, string> = {\n [HttpStatus.OK]: 'Tudo ok',\n [HttpStatus.NotFound]: 'Não encontrado',\n [HttpStatus.Error]: 'Erro do servidor'\n};\n\n3. Formulários e validação
\nRecord é excelente para armazenar erros de validação por campo:
\n\ntype FormFields = 'email' | 'password' | 'name';\ntype ValidationErrors = Record<FormFields, string | null>;\n\nconst errors: ValidationErrors = {\n email: 'Email inválido',\n password: null,\n name: 'Nome é obrigatório'\n};\n\n4. Cache de dados
\nSimulação de cache com chaves de identificador:
\n\ntype UserI