Proyek: Type-Safe App — TypeScript

Saatnya menggabungkan semua yang sudah kamu pelajari! Dalam proyek ini, kamu akan membangun type-safe data layer. Pattern yang sering dipakai: Interface untuk…

Di proyek ini kamu merakit hal-hal dari materi TypeScript sebelumnya jadi satu aplikasi kecil: katalog toko yang datanya bertipe dari ujung ke ujung. Bayangin gudang toko. Tiap barang punya kartu data dengan isian yang sama, kategorinya cuma boleh dari daftar tertentu, dan petugas gudang punya satu cara mencari yang bisa dipakai untuk isian apa pun. TypeScript menjaga supaya petugasnya gak mencari isian yang gak ada di kartu, dan gak mencari angka dengan teks.

🧪 Tebakan cepat: harga Pensil adalah angka 3000. Kalau fungsi pencarinya menerima value: any, lalu kamu memanggil filterByKey(produk, "harga", "3000") (teks, bukan angka), apa yang terjadi: TypeScript memberi error, atau hasilnya array kosong tanpa peringatan? Tebak dulu, jawabannya ada di bagian "Satu fungsi cari untuk semua kunci".

Model data: interface dan union literal

interface Produk {
  nama: string
  kategori: "buku" | "alat tulis"
  harga: number
  adaStok: boolean
}

const produk: Produk[] = [
  { nama: "Buku Tulis", kategori: "alat tulis", harga: 5000, adaStok: true },
  { nama: "Novel", kategori: "buku", harga: 85000, adaStok: true },
  { nama: "Pensil", kategori: "alat tulis", harga: 3000, adaStok: false },
  { nama: "Kamus", kategori: "buku", harga: 120000, adaStok: false },
]

Satu fungsi cari untuk semua kunci

Daripada menulis cariKategori, cariHarga dan cariStok, tulis satu fungsi generic:

function filterByKey<T, K extends keyof T>(items: T[], key: K, value: T[K]): T[] {
  return items.filter((item) => item[key] === value)
}

const hasil = filterByKey(produk, "kategori", "buku")
console.log(hasil.map((p) => p.nama)) // ["Novel", "Kamus"]

Bandingkan dengan versi value: any (yang ada di kode awal latihan):

Panggilan Hasil value: any value: T[K]
filterByKey(produk, "kategori", "buku") Novel, Kamus boleh boleh
filterByKey(produk, "harga", 3000) Pensil boleh boleh
filterByKey(produk, "harga", "3000") [] boleh ts(2345)
filterByKey(produk, "kategori", "majalah") [] boleh ts(2345)
filterByKey(produk, "hraga", 3000) [] ts(2345) ts(2345)

Dengan value: any, "3000" lolos, dan hasilnya array kosong tanpa peringatan, karena angka 3000 gak sama persis dengan teks "3000". Itu jawaban tebakannya. Dengan value: T[K], TypeScript menolak sebelum kode jalan: Argument of type 'string' is not assignable to parameter of type 'number'. ts(2345). Untuk "majalah": Argument of type '"majalah"' is not assignable to parameter of type '"alat tulis" | "buku"'. Salah ketik nama kunci tertangkap di kedua versi, berkat keyof: Argument of type '"hraga"' is not assignable to parameter of type 'keyof Produk'.

Mengubah dan mencari dengan aman

function ubahProduk(p: Produk, perubahan: Partial<Produk>): Produk {
  return { ...p, ...perubahan }
}

ubahProduk(produk[0], { harga: 4500 })    // boleh, cuma harga yang diubah
ubahProduk(produk[0], { harga: "murah" }) // error ts(2322)

const cari = produk.find((p) => p.nama === "Pensil")
if (cari) {
  console.log(cari.harga) // 3000
}

Menjalankan proyeknya

Proyeknya diatur oleh tsconfig.json, dengan "strict": true supaya semua pemeriksaan ketat nyala. Perintah npx tsc memeriksa semua file, lalu menulis JavaScript-nya. Semua tipe hilang, fungsinya tinggal begini:

function filterByKey(items, key, value) {
    return items.filter((item) => item[key] === value);
}

Versi value: any dan value: T[K] jadi JavaScript yang sama persis. Bedanya cuma di apa yang ditolak tsc sebelum kode jalan.

⚠️ Jebakan yang sering ketemu

TL;DR: Proyek type-safe dimulai dari model data: interface untuk bentuknya, union literal untuk nilai yang terbatas. Fungsi generic filterByKey<T, K extends keyof T>(items: T[], key: K, value: T[K]): T[] dengan return items.filter((item) => item[key] === value) bekerja untuk data apa pun: keyof menolak nama kunci yang gak ada, T[K] menolak nilai yang tipenya salah, sedangkan value: any meloloskannya diam-diam. Partial<T> untuk perubahan sebagian, dan hasil find dicek dengan if. Semuanya diatur tsconfig.json dengan "strict": true, diperiksa dan dikompilasi dengan npx tsc, dan tipenya hilang di JavaScript.

Yang akan kamu pelajari