Bài 2. Kiểu dữ liệu chi tiết

Bài này đi sâu vào hệ thống kiểu của TypeScript: kiểu nguyên thủy, mảng, tuple, enum, literal và bộ ba đặc biệt any, unknown, never.

45 phútCơ bản
Bạn sẽ học được
  • Phân biệt các kiểu nguyên thủy và kiểu đối tượng
  • Khai báo mảng, tuple, enum đúng cách
  • Hiểu khi nào TS tự suy luận kiểu
  • Biết vì sao nên dùng unknown thay any
Thuật ngữ cần biết
Chú thích kiểu (type annotation)
Ghi kiểu sau dấu hai chấm: let n: number.
Suy luận kiểu (inference)
TS tự đoán kiểu từ giá trị khởi tạo.
Tuple
Mảng có số phần tử và kiểu từng vị trí cố định.
Kiểu đáy (bottom type)
never: không có giá trị nào thuộc kiểu này.

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

TypeScript dùng hệ kiểu cấu trúc (structural typing): hai kiểu tương thích nếu có cùng “hình dạng”, không cần cùng tên. Kiểu chỉ tồn tại lúc biên dịch; khi chạy, mã đã thành JavaScript thuần nên kiểu bị xóa (type erasure). Vì vậy kiểu là công cụ kiểm tra, không phải cơ chế chạy.

Kiểu có thể xem như tập hợp giá trị: number là tập mọi số, "a" | "b" là tập hai chuỗi, unknown là tập mọi giá trị, never là tập rỗng. Cách nhìn này giúp hiểu union (hợp), intersection (giao) ở các bài sau.

NhómKiểuVí dụ
Nguyên thủystring, number, boolean, bigint, symbollet age: number = 20
Rỗngnull, undefinedlet x: string | null = null
Tập hợpArray, tuplenumber[], [string, number]
Đặc biệtany, unknown, never, voidfunction log(): void

Kiểu nguyên thủy và suy luận

let ten = "An";          // suy luận: string
let diem: number = 8.5;  // chú thích tường minh
let daDong = false;      // boolean
const PI = 3.14;         // kiểu literal 3.14 vì là const
let lon = 9007199254740993n; // bigint

ten = 10; // Lỗi: Type 'number' is not assignable to type 'string'

Quy tắc: để TS tự suy luận với biến cục bộ; ghi kiểu tường minh cho tham số hàm, giá trị trả về của hàm công khai và cấu trúc dữ liệu dùng chung.

Mảng và tuple

const diems: number[] = [7, 8, 9];
const tens: Array<string> = ["An", "Bình"];
const chiDoc: readonly number[] = [1, 2];
// chiDoc.push(3); // Lỗi: readonly

// Tuple: [tên, tuổi]
let sv: [string, number] = ["Chi", 19];
// Tuple có nhãn và phần tử tùy chọn
type Diem2D = [x: number, y: number, nhan?: string];
const p: Diem2D = [3, 4];

Literal, enum và as const

type TrangThai = "moi" | "dang-lam" | "xong";
let tt: TrangThai = "moi";

enum Quyen { Doc = 1, Ghi = 2, Xoa = 4 }
const q = Quyen.Ghi; // 2

// Cách hiện đại thay enum: đối tượng as const
const MAU = { Do: "#f00", Xanh: "#00f" } as const;
type Mau = typeof MAU[keyof typeof MAU]; // "#f00" | "#00f"

Enum sinh ra mã JavaScript thật khi chạy. Nhiều dự án mới (và chế độ erasableSyntaxOnly khi Node.js chạy TS trực tiếp) ưu tiên union literal hoặc as const vì không sinh mã thừa.

any, unknown, never, void

KiểuÝ nghĩaKhi dùng
anyTắt kiểm tra kiểuTránh; chỉ khi chuyển dần mã JS cũ
unknownChưa biết, phải kiểm tra trước khi dùngDữ liệu từ API, JSON.parse, input
neverKhông bao giờ có giá trịHàm luôn ném lỗi, kiểm tra vét cạn
voidHàm không trả gì hữu íchHàm xử lý sự kiện, log
const raw: unknown = JSON.parse('{"ten":"An"}');
// raw.ten; // Lỗi: 'raw' is of type 'unknown'
if (typeof raw === "object" && raw !== null && "ten" in raw) {
  console.log(raw.ten); // đã thu hẹp kiểu, dùng được
}
Tình huống: Nhận dữ liệu cấu hình

Bạn đọc cấu hình từ tệp JSON. Khai báo kết quả là unknown buộc bạn kiểm tra trường port là số trước khi dùng, tránh lỗi chạy khi tệp bị sửa sai.

Mẹo hay

Bật "strict": true trong tsconfig ngay từ đầu. Chế độ này gồm strictNullChecks, giúp bắt lỗi null/undefined, loại lỗi phổ biến nhất trong JavaScript.

Lưu ý

Dùng as (ép kiểu) chỉ là “hứa” với trình biên dịch, không kiểm tra lúc chạy. Lạm dụng as và any làm mất toàn bộ lợi ích của TypeScript.

Thực hành
  1. Khai báo tuple [string, number, boolean] mô tả sinh viên (tên, điểm, đạt).
  2. Tạo union literal cho các ngày trong tuần và hàm nhận kiểu đó.
  3. Viết hàm nhận unknown, trả về độ dài nếu là chuỗi hoặc mảng, ngược lại trả 0.
  4. Chuyển một enum sang đối tượng as const và kiểu union tương ứng.
Tóm tắt bài học
  • Kiểu là tập hợp giá trị, chỉ tồn tại lúc biên dịch.
  • Để TS suy luận ở biến cục bộ, ghi kiểu ở ranh giới hàm.
  • Ưu tiên union literal/as const thay enum.
  • Dùng unknown thay any cho dữ liệu chưa tin cậy.