fungsivoidneverMenengah3 mnt baca

void dan never pada Fungsi

Bedakan fungsi tanpa return (void) dan fungsi yang tidak pernah selesai (never).

Kenapa Ada Dua Tipe untuk "Tidak Ada"?

Bayangkan kamu memesan ojek online. Ada dua skenario aneh: pertama, driver datang, mengantar kamu, lalu pergi tanpa bilang apa-apa. Itu void: pekerjaan selesai, tapi tidak ada "hasil" yang dibawa pulang. Kedua, driver tidak pernah sampai karena motornya mogok di tengah jalan dan ia menelepon membatalkan. Itu never: pekerjaan tidak pernah selesai secara normal. TypeScript memisahkan dua konsep ini karena konsekuensi kodenya beda jauh.

void dalam Praktik Sehari-hari

Fungsi void adalah fungsi yang selesai tanpa mengembalikan nilai yang bermakna. Kamu menemukannya di mana-mana: event handler, callback forEach, middleware, fungsi logging.

typescript
function catatAktivitas(aksi: string): void {
  console.log(`[${new Date().toISOString()}] ${aksi}`);
}

["login", "logout"].forEach((aksi) => catatAktivitas(aksi));

void bukan berarti "dilarang return". Kamu boleh menulis return; kosong untuk keluar lebih awal, tapi tidak boleh me-return sebuah nilai. Ini contoh yang ditolak compiler:

typescript
function sapa(nama: string): void {
  return `Halo, ${nama}`;
  // Error: Type 'string' is not assignable to type 'void'.
}

Pesan errornya jelas: kamu berjanji tidak mengembalikan apa-apa, lalu malah mengembalikan string. Perbaikannya: hapus anotasi void bila memang butuh return, atau hapus nilai return-nya bila memang tidak butuh.

Satu jebakan klasik: mencoba memakai hasil fungsi void seolah ada nilainya.

typescript
const hasil = catatAktivitas("login");
console.log(hasil.toUpperCase());
// Error: Property 'toUpperCase' does not exist on type 'void'.

Ini justru fitur, bukan bug. Compiler menangkap logika yang salah: kamu mengira ada string yang kembali, padahal tidak.

never: Fungsi yang Tidak Pernah Kembali Normal

Fungsi never tidak pernah mencapai baris akhir secara normal. Ia hanya punya dua jalan keluar: melempar error, atau loop selamanya. Analogi gampangnya: void itu tamu yang pulang tanpa bawa oleh-oleh, never itu tamu yang tidak pernah pulang.

typescript
function gagalFatal(pesan: string): never {
  throw new Error(pesan);
}

function tungguSelamanya(): never {
  while (true) {
    // polling tanpa henti, mis. worker
  }
}

Kalau kamu mendeklarasikan never tapi badannya bisa selesai normal, compiler protes:

typescript
function puraPuraFatal(): never {
  console.log("selesai");
  // Error: A function whose declared type is neither 'void' nor 'any'
  // must return a value.
}

Kapan Bedanya Benar-benar Penting

Skenario nyata: kamu punya union status dan sebuah switch yang harus menangani semuanya. Trik never membuat compiler menjadi penjaga yang memastikan tidak ada kasus yang terlewat.

typescript
type Status = "antri" | "proses" | "selesai";

function labelStatus(s: Status): string {
  switch (s) {
    case "antri": return "Dalam antrian";
    case "proses": return "Sedang diproses";
    case "selesai": return "Selesai";
    default:
      const takTerduga: never = s;
      throw new Error(`Status tak dikenal: ${takTerduga}`);
  }
}

Di cabang default, tipe s sudah menyempit menjadi never karena semua kemungkinan habis. Kalau suatu hari ada yang menambah "batal" ke union Status tapi lupa menambah case, baris const takTerduga: never = s; langsung error saat compile. Tanpa trik ini, bug-nya baru ketahuan saat user mengklik di production. Inilah kenapa never ada: mengubah bug runtime menjadi error compile time.

Pola lain yang umum: helper validasi yang selalu throw, dipakai sebagai expression.

typescript
function wajibAda<T>(nilai: T | undefined, nama: string): T {
  if (nilai === undefined) {
    gagalFatal(`${nama} wajib diisi`);
  }
  return nilai; // compiler tahu baris ini hanya tercapai bila nilai ada
}

Karena gagalFatal bertipe never, compiler paham bahwa setelah pemanggilannya, kode di bawah hanya jalan bila kondisi tidak terpenuhi. Tanpa anotasi never, compiler akan mengira eksekusi bisa lanjut dan memaksa kamu menulis handling tambahan yang tidak perlu.

Catatan teknis: never adalah satu-satunya tipe yang assignable ke semua tipe, tapi tidak ada tipe (selain never sendiri) yang assignable ke never. Itu sebabnya trik exhaustive check di atas bekerja: hanya nilai yang "tidak mungkin ada" yang bisa masuk ke variabel never.

Tantangan

Validasi yang tidak pernah lolos diam-diam

Buat fungsi pastikanPositif(n: number): void yang melempar Error bila n <= 0 (tidak return apa-apa bila valid), dan fungsi gagalFatal(pesan: string): never yang selalu throw. Panggil keduanya dengan benar.

typescript
function pastikanPositif(n: number): void {
  // lempar bila n <= 0
}

function gagalFatal(pesan: string): never {
  // selalu throw
}

pastikanPositif(5);
console.log("lolos validasi");

Kuis Bab

Uji pemahamanmu: Fungsi Lebih Dalam

Jawab 5 soal berikut, lalu tekan "Periksa Jawaban".

1.Apa kegunaan literal type seperti "kiri" | "kanan"?

2.Kenapa komunitas modern lebih memilih literal union dibanding enum?

3.Apa itu function overload?

4.Apa beda parameter opsional (?) dan parameter default (=)?

5.Kapan memakai never sebagai return type?