Uma amostra JSON mostra os valores que apareceram numa determinada resposta, mas não prova o contrato completo de uma API. Ainda assim, transformá-la em TypeScript pode poupar trabalho repetitivo e tornar visível o que já conhecemos: propriedades, estruturas aninhadas, tipos primitivos e diferenças entre registos. O resultado é um ponto de partida para revisão, não uma especificação descoberta automaticamente.
Neste guia vamos usar o conversor de JSON para TypeScript com dois registos sintéticos. A amostra inclui null, uma propriedade ausente, um objeto aninhado e arrays vazios. Vamos interpretar a declaração gerada, verificar que participa na compilação e separar essa verificação estática da validação de futuras respostas em execução.
Delimitar o que uma amostra consegue demonstrar
Quando dois objetos de um array apresentam a mesma propriedade com tipos diferentes, o gerador pode produzir uma união. Se a propriedade falta num deles, pode marcá-la como opcional. Se existe explicitamente null, esse tipo pode ser incluído. Estas decisões resultam de evidência presente nos dados fornecidos, não de regras de negócio inferidas a partir dos nomes.
Uma propriedade presente em todos os exemplos não está necessariamente garantida em todas as respostas futuras. Da mesma forma, a ausência de um campo na amostra não prova que ele nunca exista. Casos de erro, permissões diferentes e registos antigos podem revelar estruturas que um exemplo bem-sucedido não mostra.
Arrays vazios exigem especial cuidado. Sem elementos, não existe informação para deduzir o respetivo tipo. unknown[] representa essa incerteza melhor do que inventar um tipo de domínio ou permitir qualquer operação com any[]. A declaração gerada deve ajudar a descobrir perguntas que precisam de resposta na documentação da API.
Utilizar dois registos com diferenças relevantes
Cola exatamente este JSON:
[
{
"id": 1,
"tag": "alpha",
"active": true,
"notes": [],
"meta": { "source": "api" }
},
{
"id": 2,
"tag": null,
"notes": [],
"meta": { "source": "cache", "age": 3 }
}
]
Escolhe ApiResponse como nome raiz e type como declaração. A raiz descreve um array cujos elementos combinam os dois objetos. id será number; tag incluirá string e null; active será opcional porque falta no segundo registo. notes será unknown[], pois nenhum dos arrays contém elementos.
Dentro de meta, a propriedade source aparece nos dois objetos como string, enquanto age só aparece no segundo. Por isso, meta.age torna-se opcional. O conteúdo de source não é transformado numa enumeração de api e cache: esses valores observados não demonstram que sejam as únicas possibilidades do contrato.
O código é produzido de forma determinista. Espaçamento e parênteses podem variar entre implementações equivalentes; a verificação importante é o significado da declaração e a sua aceitação pelo compilador. Não confundas uma diferença de apresentação com uma alteração real dos tipos.
Gerar, compilar e provocar uma falha controlada
Abre a ferramenta, introduz a amostra, confirma ApiResponse e seleciona type. Prime Converter e examina o resultado antes de o copiar. Verifica cada propriedade opcional, a união de tag, o array vazio e a estrutura de meta. Descarrega o ficheiro .ts para testar exatamente o artefacto que vais integrar.
Num projeto com verificação estrita, acrescenta valores compatíveis com os dois registos e executa o compilador TypeScript. Depois introduz deliberadamente uma incompatibilidade, como um id textual em vez de numérico. A primeira verificação deve passar e a segunda deve falhar. Isto demonstra que a declaração impõe restrições estáticas, sem afirmar que os dados de uma rede foram inspecionados.
Repete com interface. Como a raiz é um array, o gerador continua a precisar de um alias de tipo para a raiz. Uma raiz objeto pode ser emitida como interface; uma raiz primitiva também utiliza um alias. Escolher interface não obriga todas as construções TypeScript a começar por essa palavra.
Compreender como os tipos são combinados
O motor examina todos os elementos disponíveis do array, em vez de copiar apenas a estrutura do primeiro. Reúne as propriedades, marca com ? as que não aparecem em todos os objetos e combina os tipos observados numa união estável. A mesma lógica aplica-se aos objetos aninhados, apresentados como tipos de objeto dentro da declaração.
Os números JSON tornam-se number. O facto de os exemplos serem inteiros não demonstra uma restrição de domínio a inteiros. As strings continuam a ser string: o conversor não deduz datas, URLs especiais, enumerações ou marcas de tipo. null mantém-se explícito e não é equivalente à ausência de uma propriedade; com verificação estrita de nulos, o código consumidor precisa de tratar essa possibilidade.
As chaves que não são identificadores válidos podem ser colocadas entre aspas no resultado. Já o nome raiz precisa de ser um identificador TypeScript permitido e não reservado. Esta distinção permite descrever propriedades reais do JSON sem aceitar nomes inválidos para a declaração principal. A ordenação estável também facilita a revisão de diferenças quando a amostra muda.
Resolver erros sem deformar a amostra
Um erro de JSON deve ser corrigido antes de pensar nos tipos. Procura vírgulas finais, chaves sem aspas, parênteses retos desequilibrados ou prefixos copiados de um registo de aplicação. Um literal de objeto JavaScript não é necessariamente JSON. O guia de erros JSON apresenta um procedimento para isolar problemas de sintaxe sem alterar valores válidos.
Se o nome raiz for rejeitado, utiliza um nome como ApiResponse, sem espaços, hífenes ou início numérico. Não introduzas um caminho de ficheiro nem um URL nesse campo. Palavras reservadas também não servem. As chaves internas da amostra podem conter caracteres diferentes porque o gerador dispõe de outra forma de as representar.
A entrada está limitada a 200 KiB em UTF-8 e a 50 níveis de profundidade. Reduz o conjunto a registos representativos e sintéticos, preservando as diferenças relevantes. Não apagues estruturas de fecho para encurtar o texto. Um modelo profundamente recursivo merece um desenho próprio, em vez de uma expansão mecânica de objetos dentro de objetos.
Transformar o resultado num contrato mantido
Compara o código gerado com a documentação oficial da API e com exemplos que cubram situações diferentes: lista vazia, permissões limitadas, dados antigos, sucesso e erro. Ajusta uniões e opcionalidade de acordo com o comportamento documentado. Se um conceito aninhado se repetir, considera atribuir-lhe um nome próprio para melhorar a leitura e a manutenção.
Acrescenta validação em execução na fronteira de confiança. Os tipos TypeScript deixam de existir quando o JavaScript é executado. Uma asserção de tipo pode satisfazer o compilador sem examinar qualquer valor recebido. Analisa o JSON, valida dados desconhecidos com regras explícitas e só depois disponibiliza o resultado como um valor tipado à aplicação.
Usa dados sintéticos para este processo. Embora o conversor trabalhe no navegador, uma amostra copiada para um ficheiro, um commit ou uma captura de ecrã pode divulgar informação. Substitui identificadores e texto livre por equivalentes sem dados pessoais. Mantém a variedade estrutural necessária, sem transportar o conteúdo de clientes para exemplos permanentes.
Reconhecer o que não pode ser inferido
A ferramenta conhece apenas o JSON fornecido. Não descobre campos futuros, regras condicionais, limites numéricos, formatos semânticos ou variantes que nunca aparecem na amostra. Também não consulta a rede, não lê uma definição OpenAPI e não produz automaticamente um validador ou um descodificador de dados externos.
Uma coleção heterogénea pode gerar uma união útil, mas isso não prova que a aplicação deva aceitar livremente qualquer combinação dos campos observados. Regras como «esta propriedade existe apenas neste estado» podem exigir uniões discriminadas concebidas a partir do contrato. A amostra, por si só, pode não conter informação suficiente para estabelecer essa relação.
Do mesmo modo, um número pode representar dinheiro, duração ou um identificador, apesar de todos aparecerem como number. Uma string pode conter uma data sem fornecer regras de fuso horário. Consulta o guia de timestamp Unix quando o significado temporal for importante e documenta unidades fora da simples declaração de tipo.
Rever o ficheiro antes de o integrar
Confirma que a entrada é JSON válido, sintético e representativo. Inclui vários objetos quando quiseres observar opcionais ou uniões. Escolhe um nome raiz válido e revê todos os ?, null, arrays, objetos aninhados e unknown[]. Aceita um alias na raiz quando o valor não for um objeto, mesmo que tenhas selecionado interface.
Compila o ficheiro descarregado com as opções estritas do projeto. Acrescenta uma atribuição válida e outra deliberadamente inválida e observa os resultados. Não consideres a simples ausência de erros na declaração como prova suficiente: a utilização do tipo também precisa de ser testada.
Por fim, compara com o contrato oficial, acrescenta validação em execução onde os dados não sejam confiáveis e regista os casos usados na amostra. Quando a API mudar, volta a gerar apenas como apoio à revisão. Analisa as diferenças em vez de substituir tipos mantidos sem compreender que novas garantias, incertezas ou incompatibilidades foram introduzidas.
Fontes: TypeScript Handbook: tipos de objeto, TypeScript Handbook: uniões e interseções e RFC 8259: formato de intercâmbio JSON.