Bài 10. TypeScript với DOM và gọi API

Áp dụng TypeScript vào trang web: truy xuất phần tử DOM có kiểu, xử lý sự kiện, gọi API bằng fetch và kiểm tra dữ liệu trả về để không tin mù quáng vào máy chủ.

50 phútTrung cấp
Bạn sẽ học được
  • Lấy phần tử DOM với kiểu chính xác
  • Gán kiểu cho sự kiện
  • Viết hàm fetch tổng quát có kiểu
  • Kiểm tra dữ liệu runtime bằng type guard hoặc thư viện schema
Thuật ngữ cần biết
HTMLElement
Kiểu gốc của phần tử HTML; có kiểu con như HTMLInputElement.
Non-null assertion (!)
Khẳng định giá trị không null; nên hạn chế.
Schema validation
Kiểm tra cấu trúc dữ liệu khi chạy (ví dụ Zod, Valibot).

Khái niệm và lý thuyết

Thư viện chuẩn của TS (lib.dom.d.ts) mô tả toàn bộ API trình duyệt. document.querySelector("input") trả về HTMLInputElement | null: TS buộc bạn xử lý trường hợp không tìm thấy phần tử.

Dữ liệu từ mạng là ranh giới tin cậy: kiểu TypeScript không kiểm tra được lúc chạy. Cách đúng là coi kết quả response.json() là unknown, rồi kiểm tra trước khi gán kiểu. Thư viện schema cho phép viết một lần, vừa kiểm tra runtime vừa suy ra kiểu tĩnh.

DOM và sự kiện

const o = document.querySelector<HTMLInputElement>("#ten");
const nut = document.getElementById("gui") as HTMLButtonElement | null;
if (o && nut) {
  nut.addEventListener("click", (e: MouseEvent) => {
    e.preventDefault();
    alert("Xin chào " + o.value.trim());
  });
}
document.addEventListener("keydown", e => {
  if (e.key === "Escape") console.log("Đóng"); // e tự là KeyboardEvent
});

fetch có kiểu

interface NguoiDung { id: number; name: string; email: string }

function laNguoiDung(x: unknown): x is NguoiDung {
  return typeof x === "object" && x !== null &&
    typeof (x as any).id === "number" && typeof (x as any).name === "string";
}

async function layDanhSach(): Promise<NguoiDung[]> {
  const res = await fetch("https://jsonplaceholder.typicode.com/users");
  if (!res.ok) throw new Error("HTTP " + res.status);
  const data: unknown = await res.json();
  if (!Array.isArray(data) || !data.every(laNguoiDung))
    throw new Error("Dữ liệu không đúng định dạng");
  return data;
}

Kiểm tra bằng schema (Zod)

import { z } from "zod";
const NguoiDung = z.object({ id: z.number(), name: z.string(), email: z.email() });
type NguoiDung = z.infer<typeof NguoiDung>; // kiểu suy ra từ schema

const ds = z.array(NguoiDung).parse(await res.json());
Tình huống: Ô tìm kiếm người dùng

Tạo ô input và danh sách <ul>. Khi gõ, lọc danh sách người dùng đã tải theo tên; hiển thị “Đang tải”, “Lỗi” bằng discriminated union ở Bài 5.

Mẹo hay

Dùng tham số kiểu của querySelector (querySelector<HTMLInputElement>) thay vì as để mã gọn và vẫn giữ kiểm tra null.

Lưu ý

Toán tử ! (ví dụ getElementById("x")!) chỉ tắt cảnh báo; nếu phần tử không tồn tại, chương trình vẫn lỗi khi chạy.

Thực hành
  1. Tạo form đăng ký, đọc giá trị các input với kiểu chính xác.
  2. Viết hàm getJSON<T>(url, kiemTra) tổng quát nhận type guard.
  3. Hiển thị danh sách bài viết từ API jsonplaceholder.
  4. Thay type guard bằng schema Zod và so sánh độ dài mã.
Tóm tắt bài học
  • lib.dom cung cấp kiểu cho mọi API trình duyệt.
  • Luôn xử lý trường hợp phần tử null.
  • Dữ liệu mạng là unknown cho đến khi được kiểm tra.
  • Schema validation hợp nhất kiểm tra runtime và kiểu tĩnh.