Memakai TypeScript dengan DOM

Pahami cara TypeScript memberi tipe pada DOM browser: tipe elemen seperti HTMLInputElement, alasan getElementById bisa bernilai null, kapan type assertion aman, dan cara memberi tipe pada event handler.

Diterbitkan 20 September 20268 menit bacaOleh ACY Partner Indonesia
TypeScript bertemu DOM browser lewat tipe elemen
300 × 250Slot Iklan TersediaPasang iklan Anda di sini

Setelah paham dasar-dasar TypeScript, biasanya kamu langsung ingin membangun sesuatu yang nyata di browser. Begitu mulai, kamu pasti bertemu DOM: pohon elemen hidup yang menyusun sebuah halaman web. Kamu ambil sebuah tombol, baca isi input, lalu pasang pendengar untuk klik. Di JavaScript biasa, semua ini longgar dan gampang dimaafkan. Di TypeScript, tiba-tiba terasa lebih ketat, dan muncul garis merah di tempat yang menurutmu “jelas-jelas jalan”.

Gesekan itu bukan tanda TypeScript rewel. Itu cara TypeScript memberitahu kenyataan: apa yang sebenarnya browser kembalikan kepadamu. Setelah kamu mengenal beberapa tipe elemen dan satu aturan penting soal null, bekerja dengan DOM di TypeScript jadi tenang dan bisa ditebak. Artikel ini menjelaskan semuanya pelan-pelan, tanpa mengandaikan kamu sudah tahu banyak.

Mengingat lagi: apa itu DOM

Sebelum memberi tipe pada apa pun, mari pegang dulu konsep dasarnya. DOM (Document Object Model) adalah representasi HTML-mu di dalam memori browser. Setiap tag di halaman, dari <div> sampai <input>, berubah menjadi objek yang bisa kamu baca dan ubah lewat kode. Saat kamu menulis document.getElementById("email"), kamu sedang menjangkau pohon itu dan meminta satu objek tertentu.

Kalau ini masih baru buatmu, sebaiknya mampir dulu sebentar. Konsepnya kami bahas di Apa Itu DOM?, dan sisi praktis di JavaScript ada di DOM di JavaScript. TypeScript tidak menggantikan pengetahuan itu. Ia duduk di atasnya dan menambahkan jaring pengaman.

Setiap elemen punya tipe

Di DOM, tidak semua elemen itu jenis yang sama. Sebuah paragraf dan sebuah input memang berbagi sebagian perilaku, tetapi keduanya juga berbeda. Input punya properti value; paragraf tidak. TypeScript menangkap perbedaan ini lewat satu keluarga kecil tipe.

Di puncaknya ada HTMLElement. Ini tipe umum “elemen HTML apa saja”, dan ia membawa properti yang dimiliki semua elemen, seperti id, className, dan style. Di bawahnya ada tipe yang lebih spesifik, satu untuk hampir setiap jenis tag:

Tipe Cocok untuk HTML Tambahan penting
HTMLElement elemen apa saja id, className, style
HTMLInputElement <input> value, checked, placeholder
HTMLButtonElement <button> disabled, type
HTMLAnchorElement <a> href, target
HTMLFormElement <form> submit(), elements
HTMLImageElement <img> src, alt, width

Polanya konsisten: HTML + nama tag + Element. Sebuah <select> menjadi HTMLSelectElement, sebuah <textarea> menjadi HTMLTextAreaElement, dan seterusnya. Kamu jarang perlu menghafal ini. Editor akan menyarankannya, dan namanya mengikuti aturan di atas.

Kenapa ini penting? Karena tipe yang spesifik membuka properti yang tepat. Kalau TypeScript hanya tahu kamu memegang sebuah HTMLElement, ia tidak akan mengizinkanmu membaca .value, sebab tidak setiap elemen punya itu. Untuk membaca nilai sebuah input dengan aman, TypeScript harus yakin elemennya memang benar-benar sebuah input.

Dari mana tipe ini berasal?

Kamu tidak pernah mendefinisikan HTMLInputElement sendiri. Tipe-tipe ini sudah dibawakan TypeScript melalui deskripsi bawaan tentang browser. Selama proyekmu menyasar DOM, tipe-tipe itu memang tersedia di mana saja.

Kenapa getElementById bisa memberimu null

Inilah baris yang mengejutkan hampir semua orang:

const input = document.getElementById("email");
// tipe input: HTMLElement | null

Bagian | null itulah inti ceritanya. TypeScript memberi tipe getElementById sebagai mungkin mengembalikan null, dan itu memang tepat. Saat kodemu berjalan, bisa saja tidak ada elemen dengan id tersebut. Mungkin HTML-nya berubah, mungkin ada salah ketik, mungkin skripnya jalan sebelum elemennya ada. Browser akan mengembalikan null, dan di JavaScript biasa baris berikutnya akan jatuh dengan galat klasik “cannot read properties of null”.

TypeScript menolak membiarkan itu lolos diam-diam. Kalau kamu coba langsung memakai input, ia menghentikanmu:

input.value = "halo@example.com";
//    ^ Error: 'input' is possibly 'null'.

Ini bukan TypeScript yang menjengkelkan. Ini ia menangkap, sejak saat kamu menulis, sebuah bug yang seharusnya baru muncul di hadapan pengguna. Solusinya adalah menangani kemungkinan null secara jujur, lewat satu pemeriksaan sederhana:

const input = document.getElementById("email");

if (input) {
  // Di dalam blok ini, TypeScript tahu input bukan null.
  input.id; // aman
}

Ini disebut narrowing (penyempitan): begitu kamu memeriksa if (input), TypeScript mempersempit tipenya dari HTMLElement | null menjadi sekadar HTMLElement di dalam blok. Bahayanya tertangani, dan garis merahnya hilang.

Tapi perhatikan ada satu celah yang tersisa. Bahkan setelah pemeriksaan, input baru bertipe HTMLElement. TypeScript tetap tidak mengizinkanmu menyetel input.value, sebab elemen umum belum tentu punya value. Kita perlu memberitahunya bahwa elemen itu khususnya sebuah input. Di situlah assertion masuk.

Type assertion dengan as

Sebuah type assertion adalah caramu memberitahu TypeScript: “Aku tahu lebih banyak daripada kamu di sini. Percayalah, elemen ini sebuah HTMLInputElement.” Kamu menulisnya dengan kata kunci as:

const input = document.getElementById("email") as HTMLInputElement;

input.value = "jane.doe@example.com"; // sekarang diizinkan

Dengan assertion itu, TypeScript memperlakukan input sebagai elemen input, dan .value jadi tersedia. Ringkas dan pendek.

Tetapi ada jebakan yang harus kamu pahami, karena inilah cara orang paling sering salah memakai TypeScript. Sebuah assertion tidak memeriksa apa pun saat program berjalan. Ia tidak mengubah elemennya. Ia hanya mengubah apa yang TypeScript percayai. Kalau elemen dengan id email ternyata sebuah <div>, atau bahkan tidak ada sama sekali, kodemu tetap akan rusak saat dijalankan. Kamu cuma membungkam compiler tanpa membuat situasinya lebih aman sedikit pun.

`as` itu janji, bukan pemeriksaan

Saat kamu menulis as HTMLInputElement, kamu sedang mengambil tanggung jawab. TypeScript berhenti memperingatkanmu, tetapi ia tidak melakukan apa pun untuk membuktikan klaimmu. Kalau ternyata kamu salah, galatnya cuma berpindah dari saat menulis kode ke saat program berjalan, persis tempat yang tidak kamu inginkan.

Jadi kapan as sebenarnya boleh? Pakai saat kamu benar-benar tahu tipe elemennya dan TypeScript tidak bisa menyimpulkannya sendiri. Kasus yang umum dan masuk akal adalah mengambil input yang baru saja kamu tulis sendiri di HTML-mu. Tag-nya terlihat dengan mata kepalamu sendiri, jadi assertion-nya jujur.

Namun, kebiasaan yang lebih aman adalah menggabungkan pemeriksaan null dengan assertion yang lebih sempit hanya di tempat yang perlu:

const el = document.getElementById("email");

if (el instanceof HTMLInputElement) {
  // Tidak perlu assertion. Ini pemeriksaan nyata saat program berjalan.
  el.value = "john.doe@example.com";
}

Pemeriksaan instanceof benar-benar bertanya ke browser, saat program berjalan, “apakah ini sungguh sebuah input?” Kalau iya, TypeScript mempersempit tipenya untukmu, dan kamu tidak perlu membuat janji buta. Kalau kamu bisa memakai instanceof, utamakan itu daripada as. Pakai as hanya saat pemeriksaan nyata tidak praktis dan kamu yakin dengan markup-mu.

Catatan soal querySelector

querySelector berperilaku sedikit berbeda dan sering kali lebih enak. Ia juga bisa mengembalikan null, tetapi ia mengizinkanmu menyebut tipe yang diharapkan lewat generic:

const input = document.querySelector<HTMLInputElement>("#email");
// tipe: HTMLInputElement | null

if (input) {
  input.value = "support@acy-partner.com";
}

Di sini kamu tetap menangani null, tetapi kamu terhindar dari cast as mentah. Kamu memberitahu querySelector apa yang kamu harapkan, dan kamu tetap memastikan elemennya ditemukan. Pola ini enak dibaca dan menjagamu tetap aman.

Memberi tipe pada event handler

Tempat lain TypeScript bersinar di DOM adalah event. Saat pengguna mengeklik, mengetik, atau mengirim form, browser memberikan objek event ke fungsi callback-mu. TypeScript tahu bentuk persis objek itu, dan jenis event apa yang sedang terjadi.

Mulai dari klik:

const button = document.querySelector<HTMLButtonElement>("#save");

button?.addEventListener("click", (event: MouseEvent) => {
  console.log("diklik di", event.clientX, event.clientY);
});

Sebuah klik menghasilkan MouseEvent, jadi event membawa data khusus mouse seperti clientX. Perhatikan ?. setelah button: optional chaining itu berarti “panggil addEventListener hanya kalau button bukan null,” menangani kemungkinan null rapi dalam satu karakter.

Membaca apa yang diketik pengguna adalah tugas klasik, dan ada satu hal yang sudah dikenal di sini. event.target adalah elemen yang memicu event, tetapi TypeScript memberinya tipe yang luas, sebab secara teori elemen apa saja bisa jadi target. Untuk membaca .value, kamu mempersempitnya:

const field = document.querySelector<HTMLInputElement>("#search");

field?.addEventListener("input", (event: Event) => {
  const target = event.target as HTMLInputElement;
  console.log("pengguna mengetik:", target.value);
});

Di sini as HTMLInputElement masuk akal, karena kamu memasang pendengarnya pada sebuah input dan kamu tahu target-nya adalah input itu. Tetap saja, kalau kamu mau jalur yang lebih aman, if (event.target instanceof HTMLInputElement) sama bagusnya dan tidak butuh assertion.

Untuk form, event submit punya tipenya sendiri, dan menghentikan reload halaman bawaan biasanya jadi langkah pertama:

const form = document.querySelector<HTMLFormElement>("#signup");

form?.addEventListener("submit", (event: SubmitEvent) => {
  event.preventDefault();
  // baca field, validasi, kirim ke API-mu...
});

Biarkan inferensi bekerja

Di dalam addEventListener("click", ...), TypeScript sudah tahu event-nya adalah MouseEvent berdasarkan nama event-nya. Sering kali kamu tidak perlu menulis anotasi tipenya sama sekali. Tulis tipenya secara eksplisit hanya saat kamu mendefinisikan handler-nya terpisah dari tempat kamu memasangnya.

Menggabungkan semuanya

Berikut contoh kecil yang lengkap, yang merangkai setiap ide di atas menjadi sesuatu yang mungkin benar-benar kamu tulis: kotak pencarian yang bereaksi saat pengguna mengetik.

const form = document.querySelector<HTMLFormElement>("#search-form");
const input = document.querySelector<HTMLInputElement>("#query");
const output = document.querySelector<HTMLElement>("#result");

if (form && input && output) {
  input.addEventListener("input", () => {
    output.textContent = `Mencari: ${input.value}`;
  });

  form.addEventListener("submit", (event: SubmitEvent) => {
    event.preventDefault();
    output.textContent = `Kamu mencari: ${input.value}`;
  });
}

Perhatikan apa yang membuat ini aman. Setiap elemen diambil dengan querySelector bertipe, jadi tipenya presisi. Satu pemeriksaan if (form && input && output) membereskan ketiga kemungkinan null sekaligus, dan di dalam blok itu setiap elemen sudah persis tipe yang kita butuhkan. Kita sama sekali tidak perlu menulis cast as, karena generic dan pemeriksaan null sudah menanganinya. Inilah gaya tenang dan bisa ditebak yang kamu tuju.

Rangkuman

Bekerja dengan DOM di TypeScript bermuara pada beberapa ide yang mantap. Setiap elemen punya tipe, dari HTMLElement yang umum sampai tipe spesifik seperti HTMLInputElement yang membuka properti yang tepat. Pencarian seperti getElementById bisa mengembalikan null, dan TypeScript memaksamu menanganinya, sehingga mencegah satu kelas penuh galat saat program berjalan. Type assertion dengan as adalah janji yang kamu buat ke compiler, bukan pemeriksaan nyata, jadi pakai hanya saat kamu sungguh tahu markup-nya, dan utamakan instanceof atau querySelector bertipe dengan pemeriksaan null kapan pun kamu bisa. Terakhir, event handler datang dengan tipe event yang presisi seperti MouseEvent dan SubmitEvent, dan inferensi sering kali memberinya tipe secara otomatis.

Tidak ada satu pun dari ini yang sekadar pekerjaan tambahan. Setiap aturan memetakan ke satu bug nyata yang ia hentikan sejak saat kamu menulis kode, bukan di depan pengguna. Begitu polanya terasa alami, kamu berhenti melawan garis merah dan mulai membiarkannya menuntunmu ke kode yang memang tidak gampang rusak.

Tag:typescriptdomfrontendtipe-dataeventjavascript
728 × 90Slot Iklan TersediaPasang iklan Anda di sini

Artikel Terkait

Lihat Semua Artikel

Artikel yang Mungkin Kamu Suka

Apa Itu Framework Frontend? — chip kode dengan tag komponen
Frontend / Dasar

Apa Itu Framework Frontend?

Penjelasan ramah untuk pemula tentang apa itu framework frontend, kenapa mereka ada, masalah apa yang mereka selesaikan dibanding JavaScript biasa, dan kapan kamu memang sebaiknya memakainya.

20 Sep 20268 menit baca
Sampul fundamentals frontend bertuliskan Apa Itu Frontend dan HTML + CSS + JS
Frontend / Dasar

Apa Itu Frontend Development?

Panduan ramah pemula tentang frontend development: bagian website yang kamu lihat dan klik secara langsung. Pahami cara kerja HTML, CSS, dan JavaScript, serta bedanya frontend dengan backend.

20 Sep 20268 menit baca
Ilustrasi sampul untuk Apa yang Dikerjakan Frontend Developer
Frontend / Dasar

Apa yang Dikerjakan Frontend Developer: Panduan untuk Pemula

Penjelasan ramah pemula tentang apa yang sebenarnya dikerjakan frontend developer setiap hari: mengubah desain jadi antarmuka yang berfungsi, membangun UI yang bisa dipakai ulang, sampai urusan responsif, aksesibilitas, dan kecepatan.

20 Sep 20269 menit baca
Kartu judul bertuliskan Styling dan Interaktivitas di atas latar biru gelap ACY Partner
Frontend / Dasar

Cara Kerja Styling dan Interaktivitas

Penjelasan ramah pemula soal bagaimana CSS menyulap HTML polos jadi tampilan rapi, dan bagaimana JavaScript menambahkan perilaku — pola berpikir struktur, gaya, perilaku saat membangun halaman web.

20 Sep 20268 menit baca
Tiga lapisan front-end menyatu di satu halaman web
Frontend / Dasar

Cara HTML, CSS, dan JavaScript Bekerja Bersama

Panduan praktis untuk pemula: bikin satu tombol kecil, beri struktur dengan HTML, tampilan dengan CSS, dan perilaku dengan JavaScript, lalu lihat ketiga lapisan ini bekerja sama di halaman nyata.

20 Sep 20268 menit baca
Sampul ikhtisar praktik terbaik frontend
Frontend / Dasar

Ikhtisar Praktik Terbaik Frontend

Peta ringkas tentang apa itu frontend yang baik — HTML bermakna, CSS rapi, JavaScript secukupnya, desain responsif, performa, dan progressive enhancement, lengkap dengan arahan untuk mendalaminya.

20 Sep 20268 menit baca