Menu

JSON em TypeScript: tipar JSON.parse, JSON para interface e imports

JSON.parse retorna any, então o TypeScript confia em qualquer tipo que você dê ao resultado. Veja como tipar JSON lido, validá-lo com um type guard, transformar um exemplo de JSON em interface, importar arquivos .json e o que JSON.stringify faz com Dates, Sets e undefined.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

JSON.parse retorna any, então o TypeScript aceita qualquer tipo ao qual você atribua o resultado. Isso faz com que tipar JSON lido seja uma linha só, mas também significa que o tipo é uma promessa sua, não algo que o compilador verifica:

O segundo objeto tem age como a string "41". O TypeScript continua chamando isso de number, porque any pode ser atribuído a qualquer coisa, e o programa imprime 411. Para um JSON que o seu próprio código acabou de escrever, a anotação basta. Para dados de uma requisição, de um arquivo ou do local storage, valide.

Fazendo parse para unknown

Tipar o resultado como unknown faz o compilador exigir uma verificação antes de qualquer propriedade ser usada:

O erro é index.ts(4,13): error TS18046: 'data' is of type 'unknown'. Cada verificação que você escreve depois estreita data um pouco mais.

Validando com um type guard

Um type guard é uma função que retorna value is User. Quando ela retorna true, o TypeScript passa a tratar o valor como User, e as verificações dentro dela são verificações reais em tempo de execução:

O próprio JSON.parse lança um SyntaxError para texto malformado, então código real também o envolve em try/catch. Para payloads grandes ou aninhados, type guards escritos à mão ficam longos; bibliotecas de schema como Zod ou Valibot permitem declarar o formato uma vez e derivar dele tanto o validador quanto o tipo TypeScript.

JSON para interface TypeScript

Converter um exemplo de JSON em tipos é mecânico. Dada esta resposta:

{
    "id": 42,
    "title": "Learn TypeScript",
    "done": false,
    "owner": { "id": 7, "name": "Ada" },
    "tags": ["study", "ts"],
    "dueDate": "2024-03-15T10:30:00Z",
    "notes": null
}

Associe cada valor ao seu tipo, dê uma interface própria aos objetos aninhados e marque o que pode variar:

Um único exemplo não diz quais campos são opcionais ou podem ser null. Olhe várias respostas, ou a documentação da API, antes de decidir onde vão ? e | null.

Datas e o reviver

JSON não tem tipo de data, então as datas chegam como strings. O segundo argumento de JSON.parse, o reviver, é chamado para cada chave e pode reconstruí-las:

O parâmetro value do reviver é any, e o resultado também, então o tipo Order continua sendo confiado, e não verificado. JSON.stringify(order, null, 2) indenta a saída com dois espaços e transforma o Date de volta na sua string ISO.

JSON.stringify e o que ele perde

JSON.stringify é tipado para retornar string. Os valores que ele converte nem sempre voltam iguais, e o tipo não avisa:

ValorDepois de JSON.stringify
Datestring ISO (pelo método toJSON)
Map, Set{} (converta antes com [...set] ou Object.fromEntries(map))
undefined, funções e symbols dentro de um objetoa chave é omitida
undefined, funções e symbols dentro de um arraynull
undefined, uma função ou um symbol sozinhosundefined, não uma string
NaN, Infinitynull
bigintlança um TypeError

As regras em tempo de execução são as mesmas do JavaScript puro, explicadas em JSON no JavaScript.

Um tipo para qualquer valor JSON

Quando o código lida com JSON arbitrário, um tipo recursivo descreve exatamente o que o JSON pode conter e rejeita o que ele não pode:

Sem o comentário, a última linha é um erro de compilação, porque um objeto Date não é um JsonValue.

Importando arquivos .json

Um arquivo .json pode ser importado como um módulo, e o TypeScript infere o tipo a partir do conteúdo:

{ "name": "app", "port": 8080, "tags": ["a"] }
// CommonJS output, or a bundler
import config from "./config.json";
const port: number = config.port; // typed from the file: number

// An ES module under module: nodenext
import settings from "./config.json" with { type: "json" };

No TypeScript 7 isso funciona sem configurações extras com module definido como nodenext, node20, commonjs, esnext ou preserve. Com node16 e node18 falha com o erro TS2732, Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension., até você adicionar "resolveJsonModule": true; defini-la como false desativa imports de JSON em qualquer caso. Em um ES module com nodenext ou node20, o import precisa do atributo with { type: "json" } (erro TS1543 sem ele) e só o import default é permitido (erro TS1544 para import { port }). O tsc copia o arquivo .json importado para o outDir, ao lado do JavaScript compilado.

Perguntas frequentes

Que tipo JSON.parse retorna no TypeScript?

any. O compilador não tem como saber o que uma string contém, então const user: User = JSON.parse(text) compila seja qual for o conteúdo do texto. Atribua o resultado a unknown e valide quando os dados vierem de fora do seu programa.

Como converter JSON em uma interface TypeScript?

Pegue um exemplo representativo e escreva uma propriedade por chave: strings, números e booleanos viram string, number e boolean, um objeto aninhado vira uma interface própria, um array de objetos vira Item[] e chaves que às vezes faltam recebem ?. Geradores de código como o quicktype automatizam isso, mas confira os palpites deles com mais de um exemplo.

Como importar um arquivo JSON no TypeScript?

import config from "./config.json"; funciona no TypeScript 7 com module definido como nodenext, node20, commonjs, esnext ou preserve, e o resultado é tipado a partir do conteúdo do arquivo. Com node16 ou node18, defina também "resolveJsonModule": true. Em um ES module com nodenext ou node20, adicione o atributo que o Node exige: import config from "./config.json" with { type: "json" };.

JSON.stringify sempre retorna uma string?

O tipo dele diz string, mas JSON.stringify(undefined) e JSON.stringify(() => 1) retornam undefined em tempo de execução. Os valores dentro de objetos também são convertidos: um Date vira uma string ISO, e Map e Set viram {}.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR