Pular para o conteúdo principal

Converter JSON em TypeScript

O JSON para TypeScript gera interfaces a partir de um documento de exemplo no seu navegador. Cole todos os registros que você tiver, e não apenas um: um campo que aparece em alguns registros e não em outros só é detectável comparando exemplos, e ele se torna um membro opcional em vez de um obrigatório que falta em tempo de execução.

Só local
Estrutura
JSON de exemplo
0 B1 linhaLin. 1, Col. 1
Erro de sintaxe · Erro na linha 1, coluna 1

O documento termina cedo demais — parece incompleto.


^
Código geradosomente leitura
0 B1 linha
Se possível, cole mais de um registro. Assim, campos opcionais ausentes em algumas linhas podem ser detectados.

Como funciona o Converter JSON em TypeScript?

O gerador constrói um modelo de tipos intermediário a partir do seu exemplo e depois o renderiza, o que parece um detalhe de implementação e é o motivo de a saída estar correta. As decisões realmente difíceis — este campo é opcional, esses dois objetos são do mesmo tipo, qual é o tipo do elemento de um array misto — são respondidas uma vez, em um único lugar, em vez de serem recalculadas toda vez que um valor aparece.

Campos opcionais vêm da discordância entre registros

Dado [{"a":1}, {"a":1,"b":2}], a implementação óbvia lê o primeiro elemento e produz { a: number }, perdendo b em silêncio. A resposta certa une os conjuntos de campos de todos os elementos: a está nos dois, então é obrigatório; b está em um, então é b?: number. Esta é a maior diferença entre geradores, e é o motivo de colar um único registro produzir um tipo que descreve um registro, e não a sua API.

Formatos idênticos viram um tipo, não cinquenta

Cada objeto recebe uma impressão digital da sua estrutura — nomes de chaves, opcionalidade e a impressão digital de cada tipo de valor, independente de ordem. Um documento com cinquenta clientes, cada um com um address do mesmo formato, produz uma interface Address em vez de Address até Address50. Os nomes vêm da chave que guardava o objeto, no singular quando guardava um array, então "users": [{…}] gera User.

null é anulabilidade, não um tipo

Quando um campo é número em um registro e null em outro, a saída é number | null em vez de um tipo union com nome gerado. Quando um campo é null em todos os registros, a resposta honesta é unknown com um comentário dizendo isso — emitir null seria preciso sobre o seu exemplo e inútil sobre os seus dados.

Números são numbers, com uma nota

TypeScript tem um único tipo numérico, então inteiros e floats viram number. Um inteiro grande demais para um tipo de 32 bits ainda é sinalizado em um comentário, porque o mesmo modelo alimenta os geradores de Go, Java e C#, onde essa distinção decide entre int e int64 — e porque um ID de 64 bits chegando ao JavaScript, de todo modo, é algo que vale saber.

Exemplo

[
  { "id": 1, "name": "Ada", "deleted_at": null },
  { "id": 2, "name": "Grace", "deleted_at": null, "team": "ops" }
]

Gerado

export interface User {
  id: number;
  name: string;
  deleted_at: unknown; // only null in the sample
  team?: string;
}

Quais opções e casos-limite o Converter JSON em TypeScript suporta?

Referência técnica e opções
ParâmetroTipoPadrãoComportamento e casos-limite
Tipo raizidentificadorRootNome para o tipo de nível superior. Uma raiz de array é reportada como Nome[], e os formatos de objeto dentro dele são nomeados de acordo com as chaves que os contêm.
interface / typedeclaraçãointerfaceInterfaces suportam fusão de declarações e produzem melhores mensagens de erro em formatos de objeto; aliases de tipo são necessários se você quiser depois uniões ou tipos mapeados. Ambos são válidos aqui.
readonlybooleanooffMarca todos os membros como readonly. Vale a pena ativar para uma resposta de API parseada, que nada no seu código deve estar mutando in-place.
chaves camelCasebooleanooffRenomeia chaves em snake_case e registra a original em um comentário final. Só está correto se algo no seu pipeline realmente as renomear — o tipo gerado para de corresponder ao JSON caso contrário.
Opcionalidadeinferidaentre os registrosUma chave ausente de pelo menos um registro do mesmo formato torna-se opcional. Cole vários registros; um registro não pode expressar isso.
Anulávelinferidaunião com nullUm valor que é null em alguns registros e string em outros torna-se string | null. Ausente e null são coisas diferentes e são reportados diferentemente.
Arrays mistosuniãoentre parêntesesUm array de escalares mistos torna-se (string | number)[]. Os parênteses são importantes: string | number[] significa outra coisa completamente.
Tamanho de entradabytes2 MBA inferência lê cada valor. Algumas centenas de registros representativos descrevem um formato tão bem quanto um milhão, então o limite não custa nada na prática.

Perguntas frequentes

Por que um campo é opcional se a minha API sempre o envia?

Porque pelo menos um registro do exemplo que você colou não o tinha. O gerador só consegue descrever os dados que recebeu. Se o campo realmente está sempre presente, cole um exemplo em que ele sempre aparece — ou remova o ? à mão, que é a decisão certa quando você conhece o contrato melhor do que o exemplo.

Devo colar um registro ou a resposta inteira?

A resposta inteira, e idealmente várias. Opcionalidade, anulabilidade e tipos mistos são propriedades do conjunto de registros, não de um único. Um gerador alimentado com um registro não consegue distinguir um campo sempre presente de um que por acaso veio naquela vez, e ele produzirá com toda a confiança um tipo errado justamente do jeito mais difícil de notar.

Por que recebi unknown em vez de um tipo de verdade?

Todos os exemplos naquela posição eram null, então não há nada do que inferir. As alternativas são piores: null como tipo é preciso sobre o exemplo e inútil sobre os dados, e any desliga a checagem de tipos para tudo que vem depois dele. unknown te obriga a restringir antes de usar, que é o resultado correto quando o tipo é realmente desconhecido.

Ele gera validadores de tempo de execução, e não só tipos?

Não. Os tipos de TypeScript são apagados na compilação, então uma interface gerada a partir de um exemplo não diz nada sobre a resposta que você de fato recebeu em tempo de execução. Se você precisa da garantia, e não da anotação, gere um JSON Schema e valide contra ele, ou escreva um schema Zod à mão. Tratar uma interface gerada como validação é um erro comum e caro.

Ele lida com documentos profundamente aninhados ou recursivos?

Aninhamento, sim, até uma profundidade de 100 — além disso o código gerado seria inutilizável de qualquer forma, então há um teto claro em vez de um estouro de pilha. Recursão de verdade (um comentário que contém comentários) é inferida até onde o exemplo vai; se o exemplo tem três níveis, você recebe três níveis, porque nada nos dados diz que a estrutura se repete para sempre.

Meu JSON é enviado?

Não. Inferência e renderização rodam ambos na aba que você já tem aberta — abra o DevTools e observe o painel Network enquanto cola. Isso importa mais aqui do que na maioria das ferramentas, porque os documentos que as pessoas convertem são respostas de API de produção, muitas vezes com registros reais de clientes dentro.