Proyek Akhir: Manajer Tugas Bertipe Aman
Menggabungkan semua yang dipelajari — type alias, union literal, interface, class generik, utility types, Result type, dan penanganan error — dalam proyek Manajer Tugas yang aman tipe.
Selamat sudah sampai di sini! 🎉
Di bab terakhir kita merangkai semuanya menjadi satu program kecil yang rapi: Manajer Tugas. Kamu akan memakai tipe untuk memodelkan data, class untuk menyimpan state, utility types untuk turunan, dan Result type untuk kegagalan yang wajar.
Langkah 1 — Modelkan data lebih dulu
Aturan emas proyek TypeScript: mulai dari tipe. Setelah bentuk data jelas, kode lainnya mengikuti.
type Prioritas = "rendah" | "sedang" | "tinggi"
interface Tugas {
readonly id: number
judul: string
selesai: boolean
prioritas: Prioritas
tenggat?: string // opsional, format "2026-12-31"
}
type TugasBaru = Omit<Tugas, "id" | "selesai"> // data yang diisi pengguna
type PerubahanTugas = Partial<Omit<Tugas, "id">> // untuk edit: semua opsional, id tak boleh diubah
Langkah 2 — Tentukan bentuk hasil
type Hasil<T> =
| { ok: true; nilai: T }
| { ok: false; galat: string }
Langkah 3 — Class yang menyimpan state
class DaftarTugas {
private tugas: Tugas[] = []
private idBerikut = 1
tambah(baru: TugasBaru): Tugas {
const t: Tugas = { id: this.idBerikut++, selesai: false, ...baru }
this.tugas.push(t)
return t
}
selesaikan(id: number): Hasil<Tugas> {
const t = this.tugas.find((x) => x.id === id)
if (!t) return { ok: false, galat: `Tugas ${id} tidak ditemukan` }
t.selesai = true
return { ok: true, nilai: t }
}
ubah(id: number, perubahan: PerubahanTugas): Hasil<Tugas> {
const t = this.tugas.find((x) => x.id === id)
if (!t) return { ok: false, galat: `Tugas ${id} tidak ditemukan` }
Object.assign(t, perubahan)
return { ok: true, nilai: t }
}
daftar(filter?: { prioritas?: Prioritas; selesai?: boolean }): Tugas[] {
return this.tugas.filter(
(t) =>
(filter?.prioritas === undefined || t.prioritas === filter.prioritas) &&
(filter?.selesai === undefined || t.selesai === filter.selesai),
)
}
ringkasan(): Record<Prioritas, number> {
const hasil: Record<Prioritas, number> = { rendah: 0, sedang: 0, tinggi: 0 }
for (const t of this.tugas) hasil[t.prioritas]++
return hasil
}
}
Mengapa desain ini kuat?
readonly id+Omit<..., "id">pada input: mustahil tak sengaja mengubah atau memalsukan id.Prioritassebagai literal union: typo "tingi" langsung ditolak.Hasil<Tugas>: pemanggil dipaksa memeriksaoksebelum memakainilai.Record<Prioritas, number>pada ringkasan: jika kelak ditambah prioritas "darurat" dan lupa mengisinya, compiler langsung memberi tahu.private tugas: state hanya bisa diubah lewat metode yang menjaga aturan.
Langkah 4 — Memakai
const d = new DaftarTugas()
d.tambah({ judul: "Belajar generics", prioritas: "tinggi" })
d.tambah({ judul: "Cuci piring", prioritas: "rendah" })
const r = d.selesaikan(1)
if (r.ok) console.log("Selesai:", r.nilai.judul)
else console.log(r.galat)
Ide pengembangan setelah kursus
- Simpan ke
localStorage/ file JSON, dan validasi saat membaca (bab 22). - Tambah pengurutan berdasarkan tenggat; buat tipe
Urutan = "tenggat" | "prioritas". - Bungkus jadi API dengan Express + Zod, atau antarmuka dengan React/Vue.
- Tulis tes dengan Vitest/Jest — tipe dan tes saling melengkapi.
Jalan belajar berikutnya
- Pelajari framework: React, Vue, Next.js (semua mendukung TS penuh).
- Pelajari backend: NestJS atau Express + Prisma.
- Dalami TypeScript: Type Challenges di GitHub dan buku Effective TypeScript.
Setelah menyelesaikan latihan ini, kamu siap mengikuti Kuis Akhir (25 soal). Skor yang baik membuka sertifikat penyelesaian course.