JSON.parse mengembalikan any, jadi TypeScript menerima tipe apa pun yang kamu berikan pada hasilnya. Memberi tipe pada JSON hasil parse jadi cukup satu baris, tetapi itu juga berarti tipenya adalah janji yang kamu buat, bukan sesuatu yang diperiksa compiler:
Object kedua punya age berupa string "41". TypeScript tetap menganggapnya number, karena any bisa di-assign ke apa saja, dan program mencetak 411. Untuk JSON yang baru saja ditulis oleh kodemu sendiri, anotasi itu tidak masalah. Untuk data dari request, file, atau local storage, lakukan validasi.
Parse ke unknown
Memberi tipe unknown pada hasilnya membuat compiler mewajibkan pemeriksaan sebelum properti apa pun dipakai:
Error-nya adalah index.ts(4,13): error TS18046: 'data' is of type 'unknown'. Setiap pemeriksaan yang kemudian kamu tulis akan mempersempit tipe data sedikit demi sedikit.
Validasi dengan Type Guard
Type guard adalah fungsi yang mengembalikan value is User. Kalau fungsi itu mengembalikan true, TypeScript memperlakukan nilainya sebagai User sejak saat itu, dan pemeriksaan di dalamnya adalah pemeriksaan runtime yang sungguhan:
JSON.parse sendiri melempar SyntaxError untuk teks yang formatnya rusak, jadi kode sungguhan juga membungkusnya dengan try/catch. Untuk payload yang besar atau bersarang, type guard yang ditulis manual jadi panjang; library schema seperti Zod atau Valibot memungkinkanmu mendeklarasikan bentuknya sekali, lalu menurunkan validator dan tipe TypeScript-nya dari situ.
JSON ke Interface TypeScript
Mengubah contoh JSON menjadi tipe itu pekerjaan mekanis. Misalnya ada response seperti ini:
{
"id": 42,
"title": "Learn TypeScript",
"done": false,
"owner": { "id": 7, "name": "Ada" },
"tags": ["study", "ts"],
"dueDate": "2024-03-15T10:30:00Z",
"notes": null
}
Petakan setiap nilai ke tipenya, beri object bersarang interface-nya sendiri, dan tandai bagian yang bisa berubah-ubah:
Satu contoh data saja tidak bisa memberitahumu field mana yang opsional atau bisa bernilai null. Lihat beberapa response, atau dokumentasi API-nya, sebelum memutuskan memakai ? dan | null.
Tanggal dan Reviver
JSON tidak punya tipe tanggal, jadi tanggal datang sebagai string. Argumen kedua JSON.parse, yaitu reviver, dipanggil untuk setiap key dan bisa membangun ulang tanggal itu:
Parameter value milik reviver bertipe any, begitu juga hasilnya, jadi tipe Order tetap dipercaya, bukan diperiksa. JSON.stringify(order, null, 2) memberi indentasi dua spasi pada output dan mengubah Date kembali menjadi string ISO.
JSON.stringify dan Apa yang Hilang
JSON.stringify diberi tipe kembalian string. Nilai yang dikonversinya tidak selalu kembali dalam bentuk yang sama, dan tipenya tidak memperingatkanmu:
| Nilai | Setelah JSON.stringify |
|---|---|
Date | string ISO (lewat method toJSON-nya) |
Map, Set | {} (konversi dulu dengan [...set] atau Object.fromEntries(map)) |
undefined, fungsi, symbol di dalam object | key-nya dihilangkan |
undefined, fungsi, symbol di dalam array | null |
undefined, fungsi, atau symbol yang berdiri sendiri | undefined, bukan string |
NaN, Infinity | null |
bigint | melempar TypeError |
Aturan saat runtime sama seperti di JavaScript biasa, yang dibahas di JSON di JavaScript.
Tipe untuk Nilai JSON Apa Pun
Kalau kode menangani JSON sembarang, tipe rekursif bisa menjelaskan dengan tepat apa saja yang bisa disimpan JSON dan menolak nilai yang tidak bisa:
Tanpa komentar itu, baris terakhir adalah error kompilasi karena object Date bukan JsonValue.
Meng-import File .json
File .json bisa di-import seperti module, dan TypeScript menyimpulkan tipenya dari isinya:
{ "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" };
Di TypeScript 7, ini bisa dipakai tanpa pengaturan tambahan kalau module bernilai nodenext, node20, commonjs, esnext, atau preserve. Dengan node16 dan node18, import gagal dengan error TS2732, Cannot find module './config.json'. Consider using '--resolveJsonModule' to import module with '.json' extension., sampai kamu menambahkan "resolveJsonModule": true; mengaturnya ke false mematikan import JSON di semua tempat. Di ES module dengan nodenext atau node20, import-nya butuh atribut with { type: "json" } (tanpa atribut itu muncul error TS1543) dan hanya default import yang diizinkan (error TS1544 untuk import { port }). tsc menyalin file .json yang di-import ke outDir, di samping JavaScript hasil kompilasi.
Pertanyaan yang sering diajukan
Apa tipe kembalian JSON.parse di TypeScript?
any. Compiler tidak bisa tahu isi sebuah string, jadi const user: User = JSON.parse(text) lolos kompilasi apa pun isi teksnya. Kalau datanya berasal dari luar programmu, simpan hasilnya ke unknown lalu validasi.
Bagaimana cara mengubah JSON menjadi interface TypeScript?
Ambil contoh data yang representatif dan tulis satu properti untuk setiap key: string, angka, dan boolean dipetakan ke string, number, dan boolean, object bersarang menjadi interface tersendiri, array berisi object menjadi Item[], dan key yang kadang tidak ada diberi ?. Generator kode seperti quicktype mengotomatiskan ini, tetapi periksa tebakannya dengan lebih dari satu contoh data.
Bagaimana cara meng-import file JSON di TypeScript?
import config from "./config.json"; bisa dipakai di TypeScript 7 dengan module bernilai nodenext, node20, commonjs, esnext, atau preserve, dan hasilnya diberi tipe berdasarkan isi file. Dengan node16 atau node18, atur juga "resolveJsonModule": true. Di ES module dengan nodenext atau node20, tambahkan atribut yang diwajibkan Node: import config from "./config.json" with { type: "json" };.
Apakah JSON.stringify selalu mengembalikan string?
Tipenya menyatakan string, tetapi JSON.stringify(undefined) dan JSON.stringify(() => 1) mengembalikan undefined saat runtime. Nilai di dalam object juga dikonversi: Date menjadi string ISO, sedangkan Map dan Set menjadi {}.