Bạn đang viết một hàm tính tổng hai số. Đơn giản thôi:
function add(num1, num2) {
return num1 + num2;
}
console.log(add(1, 6)); // 7 ✅
console.log(add('1', '6')); // 16 ❌
Hàm vẫn chạy, không có lỗi nào cả. Nhưng kết quả sai hoàn toàn — vì JavaScript âm thầm ghép hai chuỗi thay vì cộng hai số. Đây là kiểu bug cực kỳ phổ biến, và cực kỳ khó phát hiện ở production.
TypeScript sinh ra để giải quyết đúng vấn đề này.
TypeScript là gì?
TypeScript là một superset của JavaScript — nghĩa là mọi code JS đều là code TS hợp lệ. Điểm khác biệt là TypeScript cho phép (và khuyến khích) bạn khai báo kiểu dữ liệu (type) cho các biến, tham số, và giá trị trả về.
TypeScript không chạy trực tiếp trên trình duyệt hay Node.js. Nó cần được compile (biên dịch) sang JavaScript thông qua trình biên dịch tsc. Quá trình này cũng là lúc TypeScript kiểm tra các lỗi type — trước khi code chạy thật.
Vấn đề trên được giải quyết thế nào?
Bằng cách khai báo num1: number và num2: number, TypeScript hiểu rằng hàm này chỉ chấp nhận số. Nếu bạn truyền vào chuỗi, trình biên dịch sẽ báo lỗi ngay — không cần chạy code mới biết sai.
Các core types cần biết
TypeScript có một số kiểu dữ liệu cơ bản thường dùng nhất. Cú pháp khai báo type đều theo dạng tênBiến: type.
number
Bao gồm mọi loại số — nguyên, thập phân, âm. Không có kiểu int hay float riêng như một số ngôn ngữ khác.
string
Chuỗi văn bản, khai báo bằng ', " hoặc template literal `.
boolean
Chỉ nhận đúng hai giá trị: true hoặc false. Không chấp nhận 0, 1, 'true'… như JavaScript vẫn hay dùng.
object
Có hai cách dùng. Cách đơn giản nhất là khai báo thẳng cấu trúc của object — gọi là object type literal:
Nếu chỉ viết object chung chung, TypeScript biết đây là object nhưng không biết bên trong có gì — nên sẽ không gợi ý được gì hữu ích. Vì vậy nên khai báo cấu trúc cụ thể thay vì dùng object trơn.
array
Có hai cách viết, cùng ý nghĩa:
Nếu mảng chứa nhiều kiểu dữ liệu, có thể dùng union type:
any
Tắt hoàn toàn tính năng kiểm tra type — TypeScript sẽ không báo lỗi dù bạn làm gì với biến đó.
Dùng any khi đang migrate từ JS sang TS và chưa muốn type ngay. Nhưng nếu lạm dụng, TypeScript trở thành JavaScript — không còn ý nghĩa gì nữa.
Type inference — TypeScript tự đoán type
Bạn không phải lúc nào cũng cần khai báo type thủ công. TypeScript đủ thông minh để tự suy luận trong nhiều trường hợp:
Vì vậy, nguyên tắc thực tế là: chỉ khai báo type khi TypeScript không tự suy ra được, chẳng hạn ở tham số hàm.
Type từ DOM — as HTMLInputElement
Khi làm việc với DOM, một tình huống hay gặp là lấy giá trị từ một <input>:
Có hai điều đáng chú ý ở đây:
as HTMLInputElement là gì? Đây gọi là type casting (ép kiểu). document.getElementById() trả về HTMLElement | null — một kiểu rất chung, không biết đây là input, button hay div.
Bằng cách thêm as HTMLInputElement, bạn nói với TypeScript: “Tin tôi đi, phần tử này là một input.” Nhờ đó TypeScript mới biết .value là hợp lệ.
num1El.value có type là gì? Là string. Dù user điền số vào ô input, .value luôn trả về chuỗi. Vì vậy nếu bạn cần tính toán, phải chuyển đổi:
Cái hay là TypeScript sẽ bắt lỗi ngay lúc compile nếu bạn quên chuyển:
Đây chính xác là lúc TypeScript phát huy tác dụng — nó nhắc bạn “ê, num1 đang là string, hàm này cần number đó”. Bạn không cần nhớ phải chuyển, TypeScript nhớ thay.
Cấu hình TypeScript với tsconfig.json
Chạy tsc --init để tạo file tsconfig.json. Đây là nơi điều chỉnh cách TypeScript hoạt động trong project. Một số field quan trọng:
"strict": true
Bật tất cả các kiểm tra nghiêm ngặt. Khi bật, TypeScript sẽ không cho phép bạn gọi hàm trên một giá trị có thể là null. Ví dụ:
Có hai cách xử lý:
Dùng ! khi bạn chắc chắn phần tử đó có trong DOM. Nếu không chắc, dùng if check sẽ an toàn hơn.
Các field khác đáng chú ý
Riêng với Cloudflare Workers, cấu hình thường trông như sau:
Lý do không có "DOM" trong lib: Cloudflare Workers chạy trên V8 isolates, không phải trình duyệt — nên không có document, window, localStorage… Nếu để "DOM", TypeScript sẽ cho rằng các API đó tồn tại, dẫn đến lỗi lúc runtime.
Lợi ích thực sự của TypeScript
Ngoài việc bắt lỗi type, TypeScript còn giúp:
Tự động gợi ý (autocomplete) chính xác hơn trong editor — biết object có những field nào, hàm nhận tham số gì Refactor an toàn hơn — đổi tên một field, TypeScript sẽ chỉ ra tất cả những chỗ bị ảnh hưởng Code tự documenting — đọc signature của hàm là biết nó nhận gì, trả về gì, không cần comment thêm Không cần “full TypeScript” ngay từ đầu
TypeScript hoàn toàn chấp nhận code JavaScript bình thường. Bạn có thể:
Bắt đầu bằng cách đổi đuôi file từ .js sang .ts Chỉ thêm type ở những chỗ quan trọng nhất (tham số hàm, giá trị trả về) Dần dần mở rộng khi cảm thấy thoải mái hơn Khi chuyển từ JS sang TS, nếu VS Code hay báo lỗi type error, dùng // @ts-ignore hoặc // @ts-expect-error thay vì :any:
Lý do nên dùng cái này thay vì :any:
:any lây lan — một biến any truyền vào hàm khác, hàm đó cũng mất type theo @ts-ignore chỉ tắt kiểm tra đúng một dòng, phần còn lại vẫn được bảo vệ Nhưng thực ra cách phổ biến nhất khi migrate từ JS sang TS là đổi tsconfig.json:
Hoặc thêm từng flag một thay vì bật strict: true hết luôn:
Workflow thực tế nhiều người dùng là: tắt strict → code chạy được → từ từ bật lại từng flag → sửa lỗi từng phần. Không cần phải hoàn hảo ngay từ đầu.
Không cần ép bản thân type mọi thứ ngay. Ngay cả any đôi khi vẫn có chỗ dùng — miễn là dùng có chủ đích, không phải vì lười.
Tóm tắt
TypeScript giải quyết một nhược điểm lớn của JavaScript: không biết lỗi type cho đến khi chạy code. Bằng cách thêm type vào tham số hàm, biến và giá trị trả về, bạn bắt được lỗi sớm hơn, viết code rõ ràng hơn, và tự tin refactor hơn.
Buổi đầu, những thứ cần nhớ:
Khai báo type cho tham số hàm là quan trọng nhất TypeScript tự suy luận type trong nhiều trường hợp, không cần khai báo thừa as HTMLInputElement để ép kiểu khi làm việc với DOM strict: true trong tsconfig là nên bật, xử lý null bằng if hoặc ! Bước tiếp theo: Type cho object và array, Union types (string | number), Interface và Type alias — những thứ giúp TypeScript thực sự tỏa sáng khi làm việc với data phức tạp.