SEJATIDIMEDIA Logo
Tersedia Sesi Konsultasi Gratis
SejatiDimedia Logo

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

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.

Lihat Silabus Lengkap
Bedanya any, unknown, dan never yang Sering Bikin Bingung

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
function fetchUser(): 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
function handleApiResponse(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
function throwError(message: string): never {
2
throw new Error(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
type Shape =
2
| { kind: "circle"; radius: number }
3
| { kind: "square"; side: number };
4
 
5
function getArea(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
function parseAny(json: string): any {
3
return JSON.parse(json);
4
}
5
 
6
// unknown, aman, maksa validasi dulu
7
function parseUnknown(json: string): unknown {
8
return JSON.parse(json);
9
}
10
 
11
// never, dipakai buat nandain kasus yang gak seharusnya kejadian
12
function parseStrict(json: string): { id: number; name: string } | never {
13
const result = JSON.parse(json);
14
if (typeof result.id !== "number" || typeof result.name !== "string") {
15
throw new Error("Format data gak sesuai");
16
}
17
return result;
18
}
18 baris•543 chars
UTF-8•Spaces: 4

Dari contoh di atas, kelihatan jelas bedanya.

PLAINTEXT Snippetplaintext
1
any
1 baris•3 chars
UTF-8•Spaces: 4
itu paling gampang dipakai tapi paling beresiko.
PLAINTEXT Snippetplaintext
1
unknown
1 baris•7 chars
UTF-8•Spaces: 4
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.

Topik Terkait:JavascriptTypescript
Timur Dian Radha Sejati

Timur Dian Radha Sejati

Penulis & Lead Engineer

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.

Punya Masalah Arsitektur atau Ingin Membangun Sistem yang Benar?

Kami siap membantu mengaudit kode, me-refactor arsitektur yang lemot, atau membangun aplikasi bisnis Anda dengan standar enterprise sejak awal.

EKSPLORASI LANJUTAN

Artikel Rekayasa Terkait

Lihat Semua
Utility Types yang Wajib Diketahui Setiap Developer TypeScript
Best Practices
5 menit baca

Utility Types yang Wajib Diketahui Setiap Developer TypeScript

Artikel penutup Series 1 ini ngebahas kumpulan utility types bawaan TypeScript yang paling sering dipake di proyek nyata, kayak `Partial`, `Required`, `Pick`, `Omit`, `Record`, dan `ReturnType`. Setiap utility type dijelasin lewat use case konkret, misalnya `Partial` buat bikin form update yang field-nya opsional, `Omit` buat bikin DTO dari model database, dan `Record` buat bikin mapping objek yang type safe. Di akhir artikel ada juga pengenalan singkat cara bikin utility type sendiri, sebagai jembatan menuju Series 2 yang bahas type level programming lebih dalam.

Baca Artikel
Generics, dari Dasar sampai Constraint
Best Practices
5 menit baca

Generics, dari Dasar sampai Constraint

Generics sering dianggap topik yang menakutkan buat pemula, padahal konsepnya sederhana banget kalau dijelasin pake analogi yang pas. Artikel ini mulai dari masalah nyata yang diselesain generics, yaitu bikin fungsi atau komponen yang reusable tanpa kehilangan informasi tipe. Pembaca bakal diajak bangun pemahaman step by step, dari generic function sederhana, generic interface, sampai penggunaan `extends` buat batasin tipe apa aja yang boleh masuk, dan default type parameter biar generic lebih fleksibel. Semua dijelasin pake contoh kode dunia nyata kayak fungsi fetch data API dan komponen wrapper.

Baca Artikel
Union, Intersection, dan Discriminated Union, Cara Memodelkan Data yang Punya Banyak Kemungkinan Bentuk
Best Practices
5 menit baca

Union, Intersection, dan Discriminated Union, Cara Memodelkan Data yang Punya Banyak Kemungkinan Bentuk

Artikel ini fokus ke salah satu fitur paling kuat di TypeScript buat memodelkan data yang bentuknya bisa berubah-ubah. Dimulai dari union type dasar buat bilang "nilai ini bisa A atau B", lanjut ke intersection type buat gabungin beberapa tipe jadi satu, sampai discriminated union yang super berguna buat memodelkan state kayak loading, success, dan error di aplikasi nyata. Ada juga pembahasan gimana TypeScript otomatis ngelakuin narrowing berdasarkan properti pembeda, jadi kode kamu lebih aman tanpa perlu banyak pengecekan manual yang rawan salah.

Baca Artikel