any dan unknown sama-sama menerima semua nilai. Bedanya ada pada apa yang bisa kamu lakukan dengan nilai itu setelahnya: any membolehkan apa saja tanpa memeriksa apa pun, sedangkan unknown hampir tidak membolehkan apa-apa sampai kamu membuktikan nilai itu apa.
Tanpa baris @ts-expect-error, u.toUpperCase() adalah error kompilasi TS18046. Pemeriksaan typeof mempersempit u menjadi string, dan di dalam blok itu semua method string tersedia.
Sekilas any vs unknown
any | unknown | |
|---|---|---|
| Menerima nilai apa pun | ya | ya |
Diberikan ke string, number, ... | ya, tanpa pemeriksaan | tidak (TS2322) |
| Membaca property, memanggil method | ya, tanpa pemeriksaan | tidak (TS18046) |
| Dipanggil sebagai fungsi | ya | tidak |
Aritmetika dan perbandingan (x * 2, x + 1, x < 5) | ya | tidak (TS18046) |
| Perlu diperiksa sebelum dipakai | tidak | ya (typeof, instanceof, in, type guard) |
| Efek pada pemeriksaan tipe | dimatikan untuk nilai itu dan semua yang disentuhnya | tetap menyala |
Dalam istilah teori tipe, unknown adalah top type: setiap tipe bisa diberikan kepadanya, dan ia hanya bisa diberikan ke unknown dan any. any adalah jalan pintas yang berlaku dua arah, bisa diberikan ke semua tipe kecuali never.
any Mematikan Pemeriksaan Tipe
Nilai bertipe any dipercaya begitu saja. Compiler menerima salah ketik, tipe yang salah, dan property yang tidak ada, lalu kesalahannya baru muncul saat runtime.
Output-nya memperlihatkan masalahnya: variabel yang dianotasi number berisi string, dan baris terakhir melempar Cannot read properties of undefined (reading 'city'). any juga menyebar. user.name bertipe any, jadi nilai apa pun yang dihitung darinya juga any, dan satu nilai tanpa tipe bisa mematikan pemeriksaan jauh dari tempat ia masuk.
unknown Mewajibkanmu Memeriksa Dulu
Dengan unknown, compiler menolak semua operasi sampai kode mempersempit nilainya. Mempersempit memakai pemeriksaan JavaScript biasa, dan di dalam setiap cabang nilainya punya tipe yang sudah diperiksa.
Pemeriksaan value === null harus datang sebelum pemeriksaan objek karena typeof null adalah "object". Setelah "id" in value, TypeScript tahu objek itu punya property id, bertipe unknown karena tidak ada yang menyebutkan isinya. String() mengubahnya menjadi teks secara eksplisit; untuk memakainya sebagai angka, kamu perlu memeriksanya dengan typeof dulu.
Kamu juga bisa melewati pemeriksaan dengan assertion, value as string, dan compiler akan menerimanya. Itu janji tanpa pemeriksaan runtime di belakangnya, jadi pilih pemeriksaan sungguhan; lihat type assertion.
Memvalidasi JSON dengan unknown
JSON.parse dideklarasikan mengembalikan any, jadi hasilnya diam-diam mematikan pemeriksaan. Beri anotasi unknown pada hasilnya dan tulis type guard yang memeriksa bentuknya sebelum kode lainnya memercayainya.
Input pertama mencetak dark at 14px, sedangkan yang kedua ditolak karena fontSize tidak ada. Dengan any, input kedua akan lolos sebagai Settings dengan fontSize bernilai undefined. Untuk skema yang besar, library validasi melakukan pekerjaan yang sama dan menurunkan tipenya untukmu.
noImplicitAny
any tidak hanya muncul karena ada yang menuliskannya. Parameter tanpa anotasi dan tanpa konteks untuk inferensi juga akan menjadi any. Opsi noImplicitAny, yang dinyalakan oleh strict, melaporkannya sebagai error:
index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.
Perbaikannya adalah anotasi, function double(x: number). Menulis x: any secara eksplisit juga bisa dikompilasi, dan memang itu intinya: any tetap terlihat di kode, sehingga bisa dicari dan ditinjau.
Tempat any Masih Diam-diam Masuk
Bahkan dengan strict, hal-hal berikut menghasilkan any tanpa kata itu muncul di kodemu:
| Sumber | Yang kamu dapat | Yang perlu dilakukan |
|---|---|---|
JSON.parse(text) | any | beri anotasi unknown, lalu validasi |
response.json() dengan tipe browser (DOM) | Promise<any> | sama seperti JSON.parse (tipe fetch milik Node sendiri sudah mengembalikan Promise<unknown>) |
| Package tanpa definisi tipe | any untuk import-nya (dengan error kecuali kamu menambahkan deklarasi) | pasang @types/... atau tulis .d.ts |
value as any | any | pakai type guard atau assertion yang tepat |
catch (e) dengan useUnknownInCatchVariables dimatikan | any | strict membuatnya unknown; biarkan seperti itu |
Variabel catch bertipe unknown dengan strict karena apa pun bisa dilempar, bukan hanya objek Error. Persempit dengan e instanceof Error sebelum membaca e.message.
Kapan any Bisa Diterima
any tidak dilarang, tetapi setiap pemakaiannya adalah titik yang tidak lagi dilindungi compiler. Pemakaian yang wajar:
- Migrasi codebase JavaScript, di mana
anymenandai bagian yang belum diberi tipe. - Kode yang tidak bisa diekspresikan dengan baik oleh sistem tipe, dijaga tetap kecil dan berada di balik signature fungsi yang bertipe.
- Kode tes yang sengaja mengirim data yang salah.
Untuk semua hal lainnya, unknown menangani kasus "aku tidak tahu tipe ini" yang sama sambil tetap menjaga pemeriksaannya. Banyak tim menegakkan ini dengan aturan lint @typescript-eslint/no-explicit-any. Tipe yang terkait, Record<string, unknown>, adalah pilihan umum untuk "objek dengan nilai yang tidak diketahui".
Pertanyaan yang sering diajukan
Apa bedanya any dan unknown di TypeScript?
Keduanya menerima nilai apa pun. Dengan any, kamu bisa melakukan apa saja pada nilai itu (membaca property, memanggilnya, memberikannya ke number) dan compiler tidak memeriksa apa pun. Dengan unknown, kamu hampir tidak bisa melakukan apa-apa sampai kamu mempersempitnya dengan pemeriksaan seperti typeof x === "string". unknown tetap menyalakan pemeriksaan tipe, itulah sebabnya ia pilihan yang lebih aman.
Kapan sebaiknya aku memakai unknown daripada any?
Setiap kali tipe sebuah nilai tidak diketahui saat kompilasi: JSON hasil parse, data dari respons jaringan, error yang ditangkap, input dari fungsi validasi. Beri tipe unknown lalu persempit. Pakai any hanya untuk pekerjaan migrasi jangka pendek atau kode yang tidak bisa dideskripsikan oleh sistem tipe.
Apa arti "Object is of type 'unknown'"?
Error TS18046 ('x' is of type 'unknown') dan TS2571 (Object is of type 'unknown', dipakai saat nilainya bukan nama biasa, misalnya load().id) berarti kamu memakai nilai unknown seolah punya tipe tertentu, misalnya dengan membaca property atau memanggil method. Periksa tipenya dulu (typeof, instanceof, Array.isArray, in, atau fungsi type guard), lalu pakai nilainya di dalam cabang yang sudah dipersempit.
Kenapa JSON.parse mengembalikan any?
Deklarasinya di library standar berbunyi parse(text: string, ...): any, karena compiler tidak bisa tahu isi sebuah string. Hasilnya diam-diam mematikan pemeriksaan untuk semua yang disentuhnya. Beri anotasi sebagai gantinya: const data: unknown = JSON.parse(text), lalu validasi sebelum dipakai.
Apa itu noImplicitAny?
Opsi compiler, bagian dari strict, yang melaporkan error saat sebuah deklarasi diam-diam mendapat tipe any karena tidak punya anotasi dan tidak ada yang bisa dijadikan dasar inferensi: TS7006 untuk parameter, TS7005 atau TS7034 untuk variabel yang tipenya tidak bisa ditentukan. Opsi ini mencegah any muncul tanpa ada yang menuliskannya. any yang ditulis secara eksplisit tetap diizinkan.