Converter JSON snake_case para camelCase com TypeScript e Generics
Por John Pires · 03 de abril de 2026

Converter JSON snake_case para camelCase com TypeScript e Generics
Por que aprender isso em 2026
Contexto prático
APIs externas ainda usam snake_case com frequência — especialmente em backends em Python ou bancos de dados legados. Frontends em TypeScript usualmente preferem camelCase nas propriedades. Converter automaticamente entre esses estilos reduz bugs, melhora autocompletar e mantém o contrato tipado entre servidor e cliente.
Analogia simples
Pense nas chaves do JSON como etiquetas de roupa com dois idiomas: snake_case é "linha de fábrica" e camelCase é "loja visual". Você quer um tradutor que funcione em ambos os sentidos, de modo que a loja mostre rótulos no idioma do frontend sem perder a validade do conteúdo.
Vantagens técnicas:
- Autocompletion no editor funcionando corretamente.
- Menos mapeamentos manuais e menos codigo repetido.
- Contratos tipados entre runtime e tipos TypeScript com menor chance de erro.
Dica: Tipos nunca transformam runtime; combine transformação em tempo de execução com tipos que descrevem o resultado para segurança completa.
Configuração do ambiente
Ferramentas mínimas
Você precisa de Node.js (>=16 recomendado), npm ou pnpm, e TypeScript (>=4.5 para garantir suporte a template literal types). Um editor como VSCode melhora a experiência com autocompletion e jump-to-definition.
Configuração prática do tsconfig
Ative configurações que ajudam a trabalhar com features avançadas: template literal types, mapped types e possibilidade de decorators se optar por usá-los. Abaixo um exemplo mínimo. Isso não é TypeScript, mas é necessário; aplique no arquivo tsconfig.json.
// tsconfig.json (parcial)
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Resultado: o TypeScript aceitará templates literais para tipos e permitirá decorators se você for usá-los.
Hello World progressivo
1) Converter uma chave isolada
Comece pequeno: uma função que transforma uma string de snake_case para camelCase. Essa função é a base para transformar chaves de objetos.
O código abaixo implementa a transformação de uma palavra e é totalmente funcional em TypeScript.
function snakeToCamel(s: string): string {
return s.replace(/_([a-z0-9])/g, (_, p1) => p1.toUpperCase());
}
// Exemplo
console.log(snakeToCamel('first_name')); // output: firstName
console.log(snakeToCamel('user_id')); // output: userId
O que acontece depois: agora você tem uma função pura que converte rótulos. Ainda falta aplicar isso recursivamente em objetos ou arrays.
2) Converter um objeto raso
Aplicar a função acima às chaves de um objeto plano (sem objetos aninhados) é direto. Veja um exemplo de implementação que retorna um novo objeto com chaves convertidas.
function keysToCamel(obj: Record<string, any>): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
result[snakeToCamel(key)] = obj[key];
}
}
return result;
}
// Exemplo
const api = { first_name: 'Ana', last_name: 'Silva' };
console.log(keysToCamel(api)); // { firstName: 'Ana', lastName: 'Silva' }
Após executar, as chaves são convertidas e o valor permanece o mesmo. Isso serve para formulários simples e respostas não aninhadas.
3) Mostrar limites do método raso
Se o JSON tem arrays ou objetos aninhados, o método acima falha ao não descer na estrutura. A próxima etapa é uma função recursiva que trata objetos aninhados e arrays.
Mini-projeto prático
Objetivo do projeto
Construir uma função genérica que: receba um JSON em snake_case e retorne um objeto em camelCase, com tipos TypeScript que representem o novo formato. A meta é combinar runtime (conversão recursiva) e tipos (DeepCamelCase) para o editor saber o formato final.
Tipos TypeScript: SnakeToCamelCase e DeepCamelCase
Aqui entramos em generics avançados: um tipo que transforma strings e outro que aplica recursão em objetos e arrays.
type SnakeToCamelCase<S extends string> = S extends `${infer Head}_${infer Tail}`
? `${Head}${Capitalize<SnakeToCamelCase<Tail>>}`
: S;
type DeepCamelCase<T> = T extends Array<infer U>
? Array<DeepCamelCase<U>>
: T extends object
? { [K in keyof T as K extends string ? SnakeToCamelCase<K> : K]: DeepCamelCase<T[K]> }
: T;
// Exemplos de tipos
type Api = { first_name: string; user_profile: { avatar_url: string } };
type Client = DeepCamelCase<Api>;
// Client será { firstName: string; userProfile: { avatarUrl: string } }
O que acontece depois: o TypeScript passa a entender que transformar um Api resulta em Client, e o editor fornece autocompletion com as chaves camelCase.
Função runtime recursiva com assinatura genérica
Agora ligamos os tipos ao runtime. A função abaixo faz a conversão de forma segura e retorna o tipo transformado usando uma asserção genérica. Ela percorre objetos, arrays e mantém primitivos inalterados.
function convertKeysToCamel<T>(input: T): DeepCamelCase<T> {
if (Array.isArray(input)) {
return input.map(item => convertKeysToCamel(item)) as unknown as DeepCamelCase<T>;
}
if (input && typeof input === 'object') {
const result: any = {};
for (const key in input as any) {
if (Object.prototype.hasOwnProperty.call(input, key)) {
const newKey = snakeToCamel(key);
(result as any)[newKey] = convertKeysToCamel((input as any)[key]);
}
}
return result as DeepCamelCase<T>;
}
return input as DeepCamelCase<T>;
}
// Uso prático
const apiResponse = {
first_name: 'Joao',
user_profile: { avatar_url: 'a.jpg', likes: [{ post_id: 1 }] }
};
const client = convertKeysToCamel(apiResponse);
// client.firstName ; client.userProfile.avatarUrl ; client.userProfile.likes[0].postId
Depois de executar, você terá um objeto com chaves camelCase e tipos que refletem essa transformação no TypeScript.
Erros comuns e armadilhas
Existem várias armadilhas quando se combina transformação runtime com tipos:
- Assumir que tipos mudam o runtime — tipos só existem no build time.
- Modificar objetos mutavelmente pode quebrar referências; prefira retornar novos objetos.
- Ignorar valores especiais como Date, Map, Set — a função acima trata apenas objetos planos e arrays.
Exemplo de bug clássico: se receber um campo null e você fizer typeof input === 'object' sem checar null, você pode iterar sobre null ou lançar erro. O código mostrado verifica input && typeof input === 'object' para evitar isso.
Dicas de performance
Para objetos muito grandes, conversão recursiva pode ser custosa. Algumas estratégias:
- Converter apenas campos acessados (lazy conversion) — usar proxies para converter sob demanda.
- Cachear transformações de um formato conhecido para reduzir trabalho repetido.
- Executar conversão no servidor se muitos clientes consumirem o mesmo dado.
Recursos para continuar estudando
Leituras recomendadas
Estude template literal types e mapped types: esses são os pilares para manipular chaves em nível de tipo. Documentação oficial do TypeScript e posts técnicos sobre Type Challenges ajudam muito.
Ferramentas e bibliotecas
Algumas bibliotecas já resolvem partes do problema, como case-converter ou change-case, mas combinar com tipos TypeScript requer trabalho adicional. Use bibliotecas quando o desempenho e robustez forem cruciais.
Exemplo prático adicional: validação com type guards
Type guards verificam em runtime se um valor tem a forma esperada. Aqui vai um pequeno type guard para checar se algo é um objeto com chave específica.
function isObject(v: unknown): v is Record<string, unknown> {
return v !== null && typeof v === 'object' && !Array.isArray(v);
}
// Uso
const data: unknown = JSON.parse('{"first_name":"Pedro"}');
if (isObject(data)) {
const converted = convertKeysToCamel(data);
// aqui o editor sabe que 'converted' tem propriedades camelCase (por inferência)
}
Depois: o type guard protege seu código antes de chamar funções que assumem objetos, evitando exceções inesperadas.
Conclusão prática
Resumo das etapas
Passos claros que você pode aplicar agora:
- Implemente e teste
snakeToCamelpara strings. - Implemente a função recursiva que converte objetos e arrays.
- Adicione tipos genéricos
SnakeToCamelCaseeDeepCamelCasepara descrever o resultado no TypeScript. - Adote type guards para validar entradas desconhecidas.
Checklist rápido de produção
Antes de usar em produção, certifique-se de:
- Tratar
nulleundefined. - Decidir o tratamento para Date/Map/Set e outros tipos especiais.
- Medir impacto de performance em payloads grandes.
Feito isso, você terá um pipeline confiável que traduz formatos de API para o estilo do seu frontend, com tipagem que ajuda o desenvolvedor e reduz erros em tempo de execução.
Implementar essa estratégia é uma habilidade valiosa para quem está começando com TypeScript e quer entregar frontends mais previsíveis e seguros. O padrão de combinar runtime + tipos é recorrente em aplicativos complexos e vale a pena dominar.
Assuntos: TypeScript, generics, utility-types, snake_case, camelcase, type-guards
Leia também
DTOs type-safe em TypeScript: decorators, generics e type guards passo a passo
Aprenda a transformar JSON em DTOs type-safe com TypeScript usando decorators, generics e type guards. Guia passo a passo para iniciantes: configuração…
EventEmitter type-safe em TypeScript com generics e mapped types
Aprenda a construir um EventEmitter type-safe em TypeScript usando generics e mapped types: do problema de any ao emitter tipado, com exemplos práticos…
Feature flags type-safe em TypeScript: configuração, generics e type guards
Aprenda a implementar feature flags type-safe em TypeScript: defina keys literais, use generics, type guards e decorators para evitar typos e validar…
Config type-safe em TypeScript: loader genérico com merges e type guards
Padrão prático para loaders de config em TypeScript: substitua casts por um loader genérico que aplica defaults, faz deep merge e valida com type guards…
IDs nominais em TypeScript: branded types, type guards e generics passo a passo
Aprenda a proteger IDs em TypeScript com branded types, type guards e generics. Do jeito errado (string genérico) ao jeito certo (fábrica + validação), com…
Sistema de plugins type-safe em TypeScript com decorators e generics
Implementação prática de um sistema de plugins type-safe em TypeScript usando decorators, generics e type guards. Padrões, armadilhas comuns e exemplos reais…