Type assertion memeriksa satu dugaan tipe pada satu waktu. Begitu sebuah nilai any bisa berisi lima atau enam tipe berbeda, rantai if v, ok := x.(T); ok bertingkat membuat kode berubah menjadi tangga panjang yang sulit dibaca. Go menyediakan bentuk khusus untuk masalah ini: type switch, statement switch yang cabangnya dinomori oleh tipe, bukan oleh nilai. Sintaks dasarnya sudah kita kenal di chapter Type Switch pada Section Kendali Alur; chapter ini membedah sisi interfacenya — tipe static variabel di tiap cabang, pencocokan nil, dan pemakaian nyata sebagai formatter.

Satu Cabang per Tipe #

Bentuk lengkapnya seperti ini:

go
switch v := x.(type) {
case int:
	// v bertipe int di sini
case string:
	// v bertipe string di sini
case []int:
	// v bertipe []int di sini
case nil:
	// x adalah interface nil sejati
default:
	// tipe lain yang tidak didaftar
}

Perhatikan dua hal. Pertama, operand switch ditulis x.(type) — kata kunci type menggantikan tipe sasaran, dan tanda kurung wajib. Kedua, v := mendeklarasikan variabel yang tipenya berbeda di setiap cabang: compiler mengganti tipe v sesuai tipe yang tertulis pada case. Type switch pada dasarnya adalah kumpulan type assertion comma-ok yang dirangkai, dengan satu pemeriksaan berurutan dan tanpa risiko panic.

Tipe Static v di Setiap Cabang #

Aturan tipe v ini yang paling sering disalahpahami. Untuk case berisi satu tipe, v memang bertipe itu. Untuk case berisi beberapa tipe sekaligus, compiler tidak bisa memilih satu, sehingga v tetap bertipe interface aslinya (any jika operand any). Percobaan memperlakukan v sebagai bool di cabang case bool, float64 gagal dikompilasi dengan pesan berikut, diverifikasi nyata pada Go 1.27:

text
invalid operation: operator ! not defined on v (variable of interface type any)

Tabel berikut merangkum aturannya:

Bentuk caseTipe static v di dalam cabang
case intint
case []int[]int
case bool, float64tipe operand (any)
case niltipe operand (any)
defaulttipe operand (any)

case nil dan Interface Kosong #

Cabang case nil hanya cocok ketika interface yang diperiksa benar-benar nil: pasangan (dynamic type, dynamic value) kosong seluruhnya. Interface yang berisi pointer nil tidak akan masuk cabang ini karena dynamic type-nya masih terisi — pembedaan yang menjadi inti jebakan pada chapter Interface nil . Jika operand type switch Anda bisa bernilai nil, daftarkan case nil secara eksplisit; tanpa cabang itu, nilai nil jatuh ke default dan sering kali berujung pada format yang membingungkan.

Program Demo: Formatter Sederhana #

Pemakaian nyata type switch yang paling umum adalah formatter: fungsi yang menerima data bertipe apa pun lalu memilih format cetak per tipe. Berkas typeswitch-demo.go membangun formatter tersebut:

go
package main

import "fmt"

// format memilih perlakuan berbeda untuk setiap tipe dinamis.
func format(v any) string {
	switch nilai := v.(type) {
	case nil:
		return "(kosong)"
	case int:
		return fmt.Sprintf("bilangan bulat %d", nilai)
	case string:
		return fmt.Sprintf("teks %q", nilai)
	case []int:
		return fmt.Sprintf("slice int %v", nilai)
	case bool, float64:
		// case multi-tipe: nilai tetap bertipe any, bukan bool/float64.
		return fmt.Sprintf("tipe lain: %v (%T)", nilai, nilai)
	default:
		return fmt.Sprintf("tipe tak dikenal: %T", nilai)
	}
}

func main() {
	nilai := []any{42, "belajar", []int{1, 2, 3}, true, 3.5, nil, struct{}{}}
	for _, v := range nilai {
		fmt.Println(format(v))
	}
}

Output yang Dihasilkan:

text
bilangan bulat 42
teks "belajar"
slice int [1 2 3]
tipe lain: true (bool)
tipe lain: 3.5 (float64)
(kosong)
tipe tak dikenal: struct {}

Mari kita telaah baris demi baris program di atas:

  1. Baris 6–7 (func format + switch nilai := v.(type)): fungsi menerima any; variabel nilai dideklarasikan langsung di header switch dan tipenya ditentukan per cabang.
  2. Baris 8–9 (case nil): menangkap interface nil sejati; elemen nil pada slice di baris 25 masuk ke sini dan tercetak sebagai (kosong).
  3. Baris 10–13 (case int, case string): di dalam kedua cabang, nilai bertipe int dan string — verb %d dan %q sah tanpa assertion tambahan.
  4. Baris 14–15 (case []int): tipe slice juga bisa menjadi cabang; %v mencetak isinya [1 2 3].
  5. Baris 16–18 (case bool, float64): cabang multi-tipe memaksa nilai tetap bertipe any, sehingga hanya %v dan %T yang bisa dipakai di baris 18.
  6. Baris 19–20 (default): menampung tipe yang tidak didaftar — struct kosong struct{}{} di baris 25 tercetak sebagai struct {} lewat verb %T.
  7. Baris 25–28 (for _, v := range nilai): iterasi slice []any berisi tujuh tipe berbeda; setiap elemen melewati format yang sama tanpa satu pun risiko panic.

Type switch menuntaskan pertanyaan “tipe apa yang sedang menempel”. Pertanyaan berikutnya justru lebih berbahaya: kapan sebuah interface layak disebut nil, dan mengapa interface berisi pointer nil tidak pernah nil? Jebakan klasik itu kita bongkar pada chapter berikutnya.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

typeswitch-demo.go

Unduh berkas
go
package main

import "fmt"

// format memilih perlakuan berbeda untuk setiap tipe dinamis.
func format(v any) string {
	switch nilai := v.(type) {
	case nil:
		return "(kosong)"
	case int:
		return fmt.Sprintf("bilangan bulat %d", nilai)
	case string:
		return fmt.Sprintf("teks %q", nilai)
	case []int:
		return fmt.Sprintf("slice int %v", nilai)
	case bool, float64:
		// case multi-tipe: nilai tetap bertipe any, bukan bool/float64.
		return fmt.Sprintf("tipe lain: %v (%T)", nilai, nilai)
	default:
		return fmt.Sprintf("tipe tak dikenal: %T", nilai)
	}
}

func main() {
	nilai := []any{42, "belajar", []int{1, 2, 3}, true, 3.5, nil, struct{}{}}
	for _, v := range nilai {
		fmt.Println(format(v))
	}
}