Gerando tipos TypeScript a partir de um Schema
Resumo: `json-schema-to-typescript` e quicktype emitem um `.d.ts` em um comando, mas é um rascunho: opcionalidade, unions, additionalProperties e datas precisam de uma passada humana.

Resumo da ópera: gere com json-schema-to-typescript ou quicktype e depois corrija à mão opcionalidade, unions, additionalProperties e datas; mantenha o Schema como fonte e os tipos como saída, impondo isso com uma etapa types:check no CI.
Um comando lhe dá um arquivo. Não lhe dá tipos corretos.
O comando único
npx json-schema-to-typescript schema.json > src/types/order.d.ts
Ele trata $ref, allOf, oneOf, anyOf e converte description em JSDoc. Para geração em lote com banner e um style escolhido, chame sua API Node a partir de um pequeno build-types.mjs.
O quicktype é a alternativa, e também consegue inferir um schema a partir de um JSON de amostra:
npx quicktype -s schema schema.json -o order.ts --lang typescript
Quatro coisas a corrigir depois
Opcionalidade. Qualquer coisa fora de required ganha ?. Mas "sempre presente, pode ser null" e "pode estar ausente" são contratos diferentes:
interface Order {
remark?: string | null; // pode estar ausente
status: string;
}
Se o backend sempre envia remark: null, remova o ? e pare de fazer todo consumidor verificar undefined.
Unions sem discriminante. oneOf normalmente vira A | B, que o TypeScript não consegue estreitar sem um campo literal. Adicione uma propriedade type fixa a cada ramo no Schema.
additionalProperties. Omitido ou true às vezes produz [k: string]: unknown, às vezes any. O primeiro é seguro, o segundo desativa silenciosamente a verificação de tipos de todo o objeto.
Formatos de data. format: "date-time" é uma string no fio. Mapeá-lo para Date significa que você também precisa desserializar, ou o valor em tempo de execução continua string enquanto o tipo mente.
Mantenha a direção fixa
Schema é a fonte, tipos são a saída. Gerar o Schema a partir de tipos escritos à mão é aceitável apenas se a regeneração for obrigatória e imposta no CI — caso contrário os dois lados divergem e ninguém sabe qual é autoritativo.
Coloque no pipeline
"types:gen": "json2ts -i schemas/ -o src/types/generated",
"types:check": "pnpm types:gen && git diff --exit-code src/types/generated"
types:check é a parte que importa: mude o Schema sem regenerar e o CI falha.
Perguntas frequentes
Por que o arquivo gerado contém any?
Normalmente vem de additionalProperties ser true ou omitido, que alguns geradores mapeiam para [k: string]: any. Substitua por unknown ou por campos explícitos, porque any desativa a verificação de tipos de todo o objeto.
Como devo corrigir campos opcionais após a geração?
Qualquer coisa fora de required ganha ?, mas "sempre presente, pode ser null" difere de "pode estar ausente". Se o backend sempre envia o campo, remova o ?.
O CI deve falhar quando o Schema e os tipos divergem?
Sim. Um script types:check que regenera e roda git diff --exit-code falha o build assim que o Schema muda sem regeneração.