Bedanya any, unknown, dan never yang Sering Bikin Bingung
Tiga tipe ini kelihatan mirip tapi sebenarnya punya karakter yang jauh berbeda. Artikel ini ngebahas `any` sebagai tipe yang bikin TypeScript "nyerah" dan matiin semua pengecekan, `unknown` sebagai alternatif yang lebih aman karena maksa kamu ngecek dulu sebelum pakai nilainya, dan `never` sebagai tipe buat sesuatu yang emang gak pernah kejadian. Ada studi kasus kapan masing-masing cocok dipakai, plus contoh bug nyata yang bisa muncul kalau salah pilih di antara ketiganya. Cocok buat kamu yang sering asal pakai `any` tanpa tau resikonya.
Timur Dian Radha Sejati
Founder SejatiDimedia•25 September 2026•8 menit baca
Silabus Seri Rekayasa
TypeScript Fundamentals
Series ini dirancang untuk membangun fondasi TypeScript yang kuat bagi pembaca yang baru mulai belajar atau ingin memperkuat pemahaman dasarnya. Dimulai dari alasan mendasar kenapa TypeScript layak dipelajari dibanding JavaScript murni, lanjut ke konsep-konsep inti seperti perbedaan any, unknown, dan never, cara kerja type inference, union dan intersection type, generics, hingga utility types yang paling sering dipakai di proyek nyata. Setiap artikel ditulis dengan pendekatan step by step, disertai contoh kode dan studi kasus dunia nyata, sehingga pembaca tidak hanya tahu "apa" tapi juga paham "kenapa" di balik setiap konsep. Series ini menjadi pondasi sebelum masuk ke topik yang lebih advance seperti type level programming dan pola arsitektur TypeScript di Series berikutnya.
Coba jujur deh. Pas pertama kali kenal TypeScript dan ketemu error yang panjang dan bikin pusing, langkah pertama yang biasanya diambil banyak orang adalah nambahin
PLAINTEXT Snippetplaintext
1
: any
1 baris•5 chars
UTF-8•Spaces: 4
di mana-mana biar errornya hilang. Kerasa lega sesaat, tapi diam-diam ini adalah awal dari masalah yang lebih besar.
Di artikel ini kita bakal bongkar tiga tipe yang sering bikin bingung pemula, yaitu
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
,
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
, dan
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
. Ketiganya kelihatan agak abstrak di awal, tapi begitu kamu paham filosofinya, kamu bakal ngerti kenapa TypeScript sengaja bikin tiga tipe ini beda-beda, bukan cuma satu tipe generik aja.
any, Si Jalan Pintas yang Berbahaya
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
itu ibaratnya tombol "matiin semua alarm" di TypeScript. Begitu sebuah variabel dikasih tipe
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
, TypeScript langsung berhenti ngecek apapun soal variabel itu. Kamu bisa manggil method apa aja, akses properti apa aja, bahkan operasi yang jelas-jelas gak masuk akal, dan TypeScript diem aja.
index.tstypescript
1
let data:any="hello";
2
3
data.toUpperCase();// aman
4
data.push(1);// TypeScript diem aja, padahal string gak punya method push
5
console.log(data.angka.desimal);// ini juga diem aja
5 baris•182 chars
UTF-8•Spaces: 4
Kode di atas bakal error pas dijalankan, tapi TypeScript sama sekali gak ngasih peringatan pas kamu nulisnya. Ini yang bikin
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
berbahaya. Kamu kehilangan seluruh manfaat utama TypeScript, yaitu deteksi bug sebelum kode dijalankan, padahal secara sintaks kode kamu masih "kelihatan" seperti TypeScript.
Masalahnya lagi,
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
itu sifatnya menular. Kalau ada satu variabel
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
yang dipakai buat bikin variabel lain, variabel lain itu ikut jadi
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
juga, kecuali kamu kasih anotasi eksplisit. Ini bisa bikin
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
menyebar pelan-pelan ke seluruh codebase tanpa kamu sadari.
index.tstypescript
1
functionfetchUser():any{
2
return{ name:"Andi", age:25};
3
}
4
5
const user =fetchUser();// user jadi any
6
const userName = user.name;// userName juga jadi any
6 baris•163 chars
UTF-8•Spaces: 4
Kapan
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
boleh dipakai? Sebenarnya ada kondisi tertentu, misalnya pas kamu lagi migrasi codebase JavaScript besar ke TypeScript secara bertahap, dan butuh "jalan pintas sementara" biar gak semua error muncul sekaligus. Tapi ini harus dianggap utang teknis, bukan solusi permanen. Kalau memungkinkan,
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
biasanya jadi pilihan yang jauh lebih aman.
unknown, Versi Aman dari any
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
itu mirip
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
dalam artian, dia juga bisa nampung nilai apa aja. Bedanya, TypeScript gak ngizinin kamu langsung "make" nilai bertipe
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
sebelum kamu ngecek atau mastiin dulu bentuknya kayak apa.
index.tstypescript
1
let data:unknown="hello";
2
3
data.toUpperCase();// Error, Object is of type 'unknown'
3 baris•87 chars
UTF-8•Spaces: 4
Kelihatan kayak lebih ribet ya? Tapi justru di situ letak keamanannya. TypeScript maksa kamu buat mikir dulu, "oke, nilai ini sebenarnya apa sih bentuknya," sebelum kamu bisa pakai. Caranya biasanya lewat narrowing, misalnya pakai
PLAINTEXT Snippetplaintext
1
typeof
1 baris•6 chars
UTF-8•Spaces: 4
atau
PLAINTEXT Snippetplaintext
1
instanceof
1 baris•10 chars
UTF-8•Spaces: 4
.
index.tstypescript
1
let data:unknown="hello";
2
3
if(typeof data ==="string"){
4
data.toUpperCase();// aman, TypeScript udah tau ini string
5
}
5 baris•125 chars
UTF-8•Spaces: 4
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
ini paling sering dipakai buat kasus di mana kamu bener-bener gak tau bentuk data dari awal, misalnya response dari API eksternal, hasil
PLAINTEXT Snippetplaintext
1
JSON.parse
1 baris•10 chars
UTF-8•Spaces: 4
, atau input dari user yang belum divalidasi.
index.tstypescript
1
functionhandleApiResponse(response:unknown){
2
if(
3
typeof response ==="object"&&
4
response !==null&&
5
"status"in response
6
){
7
console.log(response.status);
8
}
9
}
9 baris•186 chars
UTF-8•Spaces: 4
Bandingin sama kalau kamu pakai
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
di situasi yang sama. Dengan
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
, kamu bisa aja lupa ngecek
PLAINTEXT Snippetplaintext
1
status
1 baris•6 chars
UTF-8•Spaces: 4
beneran ada atau gak, dan errornya baru ketahuan pas runtime. Dengan
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
, TypeScript maksa kamu ngecek dulu, jadi kesalahan kayak gitu ketahuan dari awal.
Aturan simpelnya, kalau kamu bener-bener gak tau tipe data dari sesuatu, pakai
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
, bukan
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
. Kamu tetep dapet fleksibilitas buat nampung data apa aja, tapi tetep aman karena dipaksa validasi dulu.
never, Tipe buat Sesuatu yang Emang Gak Pernah Kejadian
Kalau
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
dan
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
sama-sama soal "gak tau tipe data ini apa",
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
justru kebalikannya.
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
dipakai buat merepresentasikan sesuatu yang secara logika emang gak pernah terjadi.
Contoh paling gampang, fungsi yang selalu throw error dan gak pernah beneran return apa-apa:
index.tstypescript
1
functionthrowError(message:string):never{
2
thrownewError(message);
3
}
3 baris•75 chars
UTF-8•Spaces: 4
Fungsi ini gak pernah "selesai" secara normal. Dia selalu berakhir dengan throw, jadi tipe return-nya
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
, bukan
PLAINTEXT Snippetplaintext
1
void
1 baris•4 chars
UTF-8•Spaces: 4
. Bedanya sama
PLAINTEXT Snippetplaintext
1
void
1 baris•4 chars
UTF-8•Spaces: 4
,
PLAINTEXT Snippetplaintext
1
void
1 baris•4 chars
UTF-8•Spaces: 4
berarti fungsi selesai tapi gak return nilai apa-apa yang berguna.
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
berarti fungsi gak pernah selesai sama sekali.
Penggunaan
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
yang paling sering dipakai di kode sehari-hari justru buat exhaustiveness check, alias mastiin semua kemungkinan kasus di sebuah union type udah ditangani semua.
index.tstypescript
1
typeShape=
2
|{ kind:"circle"; radius:number}
3
|{ kind:"square"; side:number};
4
5
functiongetArea(shape: Shape):number{
6
switch(shape.kind){
7
case"circle":
8
return Math.PI* shape.radius **2;
9
case"square":
10
return shape.side **2;
11
default:
12
const _exhaustiveCheck:never= shape;
13
return _exhaustiveCheck;
14
}
15
}
15 baris•360 chars
UTF-8•Spaces: 4
Coba perhatiin bagian
PLAINTEXT Snippetplaintext
1
default
1 baris•7 chars
UTF-8•Spaces: 4
. Kalau semua kemungkinan
PLAINTEXT Snippetplaintext
1
kind
1 baris•4 chars
UTF-8•Spaces: 4
udah ditangani di atas, maka pas sampai ke
PLAINTEXT Snippetplaintext
1
default
1 baris•7 chars
UTF-8•Spaces: 4
, TypeScript tau
PLAINTEXT Snippetplaintext
1
shape
1 baris•5 chars
UTF-8•Spaces: 4
gak mungkin punya tipe apa-apa lagi, makanya tipenya jadi
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
. Trik ini berguna banget, karena begitu suatu hari kamu nambahin varian baru ke
PLAINTEXT Snippetplaintext
1
Shape
1 baris•5 chars
UTF-8•Spaces: 4
, misalnya
PLAINTEXT Snippetplaintext
1
{ kind: "triangle"; base: number; height: number }
1 baris•50 chars
UTF-8•Spaces: 4
, tapi lupa nambahin case buat itu di
PLAINTEXT Snippetplaintext
1
getArea
1 baris•7 chars
UTF-8•Spaces: 4
, TypeScript bakal langsung error di baris
PLAINTEXT Snippetplaintext
1
_exhaustiveCheck
1 baris•16 chars
UTF-8•Spaces: 4
. Ini semacam pengingat otomatis, "hei, ada kasus baru yang belum kamu tangani."
Ngeliat Ketiganya Berdampingan
Biar lebih kebayang, coba bandingin ketiga tipe ini lewat satu skenario yang sama, yaitu proses ambil data dari luar sistem, misalnya API.
index.tstypescript
1
// any, gak aman, semua pengecekan mati
2
functionparseAny(json:string):any{
3
returnJSON.parse(json);
4
}
5
6
// unknown, aman, maksa validasi dulu
7
functionparseUnknown(json:string):unknown{
8
returnJSON.parse(json);
9
}
10
11
// never, dipakai buat nandain kasus yang gak seharusnya kejadian
itu jalan tengah yang aman, kamu tetep fleksibel tapi dipaksa validasi.
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
itu spesifik banget buat kasus "ini seharusnya gak pernah kejadian", entah karena fungsi selalu throw, atau karena semua kemungkinan udah ditangani semua.
Kesimpulan
Kalau kamu lagi bingung mau pakai tipe apa buat data yang gak jelas bentuknya, coba pakai aturan simpel ini. Kalau kamu males mikirin tipe dan cuma mau errornya hilang, itu tandanya kamu lagi pakai
PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
, dan ini sebaiknya dihindari kecuali kepepet banget. Kalau kamu emang gak tau bentuk datanya dari awal tapi tetep pengen aman, pakai
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
. Dan kalau kamu pengen mastiin semua kemungkinan kasus udah ditangani, atau nandain fungsi yang emang gak pernah selesai normal, pakai
PLAINTEXT Snippetplaintext
1
never
1 baris•5 chars
UTF-8•Spaces: 4
.
Ketiga tipe ini kelihatan kecil, tapi paham bedanya bakal nyelametin kamu dari banyak bug diam-diam yang justru pengen dihindari TypeScript sejak awal.
Di artikel selanjutnya, kita bakal bahas soal type inference, gimana TypeScript sebenarnya udah cukup pinter buat nebak tipe data sendiri, dan kapan sebaiknya kamu tetep nulis anotasi tipe secara manual meskipun sebenarnya gak wajib.
Software engineer dan konsultan sistem di SejatiDimedia. Berfokus pada perancangan arsitektur berkinerja tinggi, refactoring backend skala enterprise, hingga pengembangan aplikasi mobile, web modern dan integrasi AI.