Ir para o conteúdo
jsonbeautifiers
Português

JSON para TypeScript

Interfaces mescladas a partir de todos os elementos do array, para que o opcional seja realmente opcional.

JSON
TypeScript

Nada do que você cola sai do seu navegador. A lista de permissões connect-src transforma isso em uma garantia do navegador, e não em uma promessa. Confira você mesmo

Transforme um payload JSON em interfaces TypeScript. Objetos aninhados viram tipos com nome próprio, arrays são mesclados a partir de todos os elementos, e formatos estruturalmente idênticos são emitidos uma vez e reaproveitados.

Inferir a partir de uma amostra é chute. O ponto desta página é que os chutes ficam visíveis.

Campos opcionais, que é onde a maioria dos geradores falha

Um gerador que lê o elemento zero de um array e para produz uma interface em que todo campo é obrigatório. O primeiro registro que omite um campo opcional então falha na checagem de tipos, e você acaba editando os tipos gerados à mão, o que derruba o propósito da coisa.

Este mescla todos os elementos. Uma chave presente em todos é obrigatória; uma chave presente em alguns é marcada como opcional com um ponto de interrogação. Isso é informação de verdade, extraída da sua amostra, que um gerador de um elemento só joga fora.

As outras decisões

null vira uma união
Um campo visto como string e como null é string | null, não any e não string.
Um array vazio é unknown[]
Não any[]. Um array vazio não carrega informação de tipo, e any[] desligaria em silêncio a checagem para tudo que encostasse nele.
Formatos repetidos são deduplicados
Uma lista de quinhentos registros produz uma interface, não quinhentas.
Nomes de elemento de array vão para o singular
Um array categories produz uma interface Category.
Identificadores inválidos ganham aspas
Uma chave has-dash, 2fast ou class vira um nome de propriedade entre aspas.
Inteiros inseguros são sinalizados
Um campo tipado como number quando a amostra tinha um inteiro acima de 2^53-1 é mentira, porque o JavaScript não consegue representá-lo. A nota diz isso; a correção certa é lá atrás, mandando como string.

O que um tipo gerado não te dá

Uma interface TypeScript é apagada em tempo de execução. Ela diz ao compilador o que você espera e não faz absolutamente nada quando a API manda outra coisa. Para isso você precisa de um validador em tempo de execução, e o fluxo honesto para uma API externa é um schema que valida e infere o tipo a partir da mesma definição.

Zod, Valibot e ArkType fazem isso. Gere uma interface aqui para entender um payload, e depois escreva o schema de execução para a fronteira que você não controla.

How to do this in code

A mesma ideia em código, e o que usar em uma fronteira em que você não confia.

sh quicktype

O quicktype suporta várias linguagens de destino. Passar várias amostras é a opção que realmente importa.

npx quicktype --lang ts --just-types --src-lang json payload.json

# Several samples, which is what makes optionality accurate
npx quicktype --lang ts --just-types samples/*.json
ts Zod

Este é o padrão para a fronteira de uma API. A interface conta para o compilador; o schema diz a verdade.

import { z } from 'zod';

const User = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  verifiedAt: z.string().nullable(),
  roles: z.array(z.string()),
});

// One definition, both a runtime check and a static type
type User = z.infer<typeof User>;

const result = User.safeParse(await res.json());
if (!result.success) console.error(result.error.issues);
ts Só tipos, a partir de um literal
// If the data is a constant you control, TypeScript can infer
// the type without a generator at all.
const config = {
  retries: 3,
  endpoints: ['a', 'b'],
} as const;

type Config = typeof config;

Perguntas frequentes

Por que um campo saiu opcional se a minha API sempre manda?
Porque pelo menos um registro da amostra não tinha esse campo. Isso é ou um campo opcional de verdade ou uma amostra incompleta. Cole um array com mais registros e a resposta melhora.
Devo usar interface ou type?
Para formatos de objeto quase não muda nada. Interfaces suportam declaration merging e produzem mensagens de erro um pouco melhores; aliases de tipo conseguem expressar uniões e tipos mapeados. Os dois estão disponíveis acima.
Esses tipos validam alguma coisa em tempo de execução?
Não. Tipos do TypeScript são apagados na compilação. Eles descrevem o que você acredita; não conferem. Para dados que atravessam uma fronteira de rede, use um validador em tempo de execução.