Bài 1 · Cơ bản · 16 phút
Vì sao TypeScript?
Biên soạn bởi Nguyễn Anh Tuấn
TypeScript là một lớp kiểu (type) phủ lên JavaScript: bắt lỗi trước khi chạy, gợi ý code, refactor an toàn. Bản chất under-the-hood: trình biên dịch tsc XOÁ sạch kiểu khi dịch ra JS (type erasure) nên kiểu không tồn tại lúc chạy - kèm khung so sánh TS gõ vào với JS xoá kiểu đi ra.
JavaScript rất dễ tính. Bạn gõ nhầm tên một thuộc tính, nó không hề báo lỗi - chỉ lặng lẽ trả về undefined, rồi cái sai trôi tiếp tới tận
lúc chạy. Xem thử:
bug.js
const meo = { ten: "Bo", tuoi: 2 };
console.log("Sang nam meo " + (meo.tuoii + 1)); Kết quả khi chạy
Sang nam meo NaN
Thấy lỗi chưa? tuoii bị gõ thừa một chữ i. JavaScript đọc meo.tuoii ra undefined, rồi undefined + 1 ra NaN. Không một lời cảnh báo. Nếu đoạn này nằm sâu trong
ứng dụng, lỗi có thể trôi tới tận tay người dùng mới lộ.
- ▸JavaScript chỉ phát hiện loại lỗi này lúc CHẠY, thường là quá muộn.
- ▸Gõ nhầm thuộc tính cho ra undefined một cách lặng lẽ, rồi lan thành NaN.
- ▸Lỗi bắt càng muộn càng đắt: muộn nhất là khi người dùng gặp.
Ai tạo ra TypeScript?
TypeScript là JavaScript cộng thêm một lớp kiểu (type). Bạn nói cho công cụ biết "biến này là số, hàm này
nhận chuỗi", và trình biên dịch tsc kiểm tra những lời hứa đó trước khi chạy. Vẫn đoạn code y hệt, nhưng lưu thành .ts:
bug.ts (giống hệt bug.js bên trên)
const meo = { ten: "Bo", tuoi: 2 };
console.log("Sang nam meo " + (meo.tuoii + 1)); tsc báo lỗi ngay khi gõ, chưa chạy dòng nào
error TS2551: Property 'tuoii' does not exist on type
'{ ten: string; tuoi: number; }'. Did you mean 'tuoi'? tsc không chỉ chặn lỗi, nó còn đoán đúng ý bạn: "Did you mean 'tuoi'?". Cùng một lớp kiểu đó cho bạn hai món quà nữa khi viết code: gợi ý (autocomplete) chính xác vì editor biết hình dạng dữ liệu, và đổi tên (refactor) an toàn vì TS tìm ra mọi chỗ dùng tới.
- ▸tsc kiểm kiểu lúc biên dịch, bắt lỗi ngay khi gõ thay vì lúc chạy.
- ▸Lớp kiểu là tài liệu sống: editor gợi ý đúng và refactor không sợ sót.
- ▸Cùng một code, đổi đuôi .ts là có thêm một tấm lưới an toàn.
Đây là điều nhiều người dùng TypeScript cả năm vẫn chưa nắm rõ. tsc không "chạy" kiểu của bạn - nó xoá sạch kiểu rồi sinh ra JavaScript thuần. Lúc chạy, không còn TypeScript ở đâu cả, chỉ có JS. Người ta gọi đó là type erasure (xoá kiểu). Bấm thử từng ví dụ:
function chao(ten: string): string {
return "Xin chao, " + ten;
}
const loi: string = chao("Bo");function chao(ten) {
return "Xin chao, " + ten;
}
const loi = chao("Bo");Bị xoá: Mọi chú thích : string bay sạch; phần chạy còn lại y hệt JavaScript.
tsc còn thêm dòng "use strict"; chuẩn ở đầu file, đây lược cho gọn.
Hệ quả rất quan trọng: kiểu không làm chậm lúc chạy (vì đã biến mất),
và bạn không thể hỏi một type lúc chạy ("biến này có đúng kiểu User không?")
- vì tới lúc đó type đã không còn. Nhưng để ý ví dụ enum: nó
là ngoại lệ, tsc sinh hẳn một object thật nên enum vẫn sống tới runtime.
Trung thực
--target es2020 và lược dòng "use strict"; cho gọn. tsc thật có nhiều tuỳ chọn hơn, và không phải mọi thứ đều bị xoá: enum, class, namespace và parameter property đều sinh ra code JavaScript thật. Quy tắc đúng là: phần "chỉ để mô tả kiểu" thì bị xoá, phần sinh ra giá trị/hành vi thì còn.- ▸tsc biên dịch TS thành JS thuần và xoá toàn bộ thông tin kiểu (type erasure).
- ▸Vì kiểu biến mất, không có chi phí lúc chạy và không kiểm được type ở runtime.
- ▸Ngoại lệ: enum/class sinh ra code thật, nên chúng còn sống sau khi biên dịch.
Đủ lý thuyết, ta cài thật. TypeScript là một gói npm; tsc là
trình biên dịch. Viết một file .ts rồi dịch ra .js:
hello.ts
const ten: string = "the gioi";
console.log("Xin chao, " + ten); Trong terminal
npm install -D typescript
npx tsc hello.ts # sinh ra hello.js (kiểu đã bị xoá)
node hello.js Kết quả khi chạy
Xin chao, the gioi
Muốn cấu hình một lần cho cả dự án, dùng file tsconfig.json (chạy npx tsc --init để sinh bản đầy đủ). Bản tối thiểu:
tsconfig.json tối thiểu
{
"compilerOptions": {
"target": "es2020",
"module": "nodenext",
"strict": true,
"outDir": "dist"
}
} Mẹo: thử nhanh không cần cài
typescriptlang.org/play: gõ TS bên trái, thấy ngay JS sinh ra và lỗi kiểu bên
phải - đúng như widget xoá kiểu ở Bước 3.Bạn vừa nắm điều quan trọng nhất: TypeScript là một lớp kiểm tra lúc biên dịch rồi tan đi, để lại JavaScript chạy. Từ đây, cả khoá xoay quanh việc dùng lớp đó cho thật giỏi: bắt đầu từ kiểu cơ bản và suy luận kiểu, tới interface và structural typing, generics, rồi lập trình hẳn ở tầng kiểu - và khép lại bằng cách di chuyển một dự án JS sang TS.
Cảm hứng từ TypeScript Cookbook
typescript-cookbook.com.Bài tiếp theo: Kiểu cơ bản & suy luận
Câu hỏi thường gặp
Không. Vì kiểu bị xoá hết khi biên dịch (type erasure), thứ thực sự chạy là JavaScript thuần - không có lớp kiểm tra kiểu nào lúc chạy. TypeScript chỉ thêm một bước biên dịch (build) lúc bạn phát triển, không thêm gánh nặng lúc chạy.
Mặc định là không: chúng chỉ hiểu JavaScript, nên cần biên dịch .ts ra .js trước (bằng tsc). Có vài công cụ chạy .ts cho tiện khi phát triển (như tsx, ts-node hoặc Bun), nhưng bên dưới chúng vẫn lược kiểu rồi mới chạy JS.
Nên giỏi JS trước. TypeScript là JavaScript cộng thêm kiểu, không phải một ngôn ngữ khác. Khoá này giả định bạn đã qua hai khoá JavaScript, nên sẽ không dạy lại vòng lặp, hàm hay object - mà tập trung vào phần kiểu.
Tick những điều em tự tin làm được. Càng lên cao, em càng hiểu sâu.
Trả lời vài câu để chắc rằng em đã nắm bài.
TypeScript khác JavaScript ở điểm cốt lõi nào?
- 1
Bắt con bug NaN
Tự viết một đoạn JavaScript có lỗi gõ nhầm tên thuộc tính làm kết quả ra NaN hoặc undefined. Chạy bằng node để thấy nó KHÔNG báo lỗi gì.
Hoàn thành khi: Một file .js chạy ra NaN/undefined vì gõ nhầm, kèm một câu giải thích vì sao JavaScript im lặng cho qua.
- 2
Để TypeScript bắt giúp
Mở TypeScript Playground (typescriptlang.org/play), dán đoạn lỗi ở bài trên vào, xem TypeScript chỉ ra lỗi gì.
Hoàn thành khi: Chép lại thông báo lỗi TypeScript đưa ra, và chỉ rõ nó bắt đúng chỗ gõ nhầm.
- 3
Cài và biên dịch
Cài TypeScript bằng npm i -D typescript, viết một file hello.ts có chú thích kiểu, biên dịch thành hello.js rồi chạy bằng node.
Hoàn thành khi: Có file hello.js do tsc sinh ra, và chạy node hello.js in ra đúng kết quả mong đợi.
- 4
Soi type erasure
Viết một file .ts có một interface và vài biến chú thích kiểu. Biên dịch rồi mở file .js sinh ra, liệt kê những gì đã biến mất.
Hoàn thành khi: Danh sách cụ thể các dòng/cú pháp có trong .ts mà không còn trong .js (vd cả khối interface và các chú thích : string).
- 5
Bắt ngoại lệ enum
Viết một enum nhỏ trong .ts, biên dịch, mở file .js. Mô tả enum khác interface ở chỗ nào.
Hoàn thành khi: Một câu kết luận: enum sinh ra code JavaScript thật (còn lúc chạy), trong khi interface biến mất hoàn toàn.
- 6
Dự đoán trước khi biên dịch
Viết một đoạn TS ngắn (5-8 dòng). TRƯỚC khi biên dịch, tự viết ra JS mà mèo con nghĩ tsc sẽ sinh. Rồi biên dịch và so.
Hoàn thành khi: Bản dự đoán JS của mèo con đặt cạnh JS thật từ tsc, kèm ghi chú chỗ nào đoán đúng, chỗ nào sai.