Bài 10.3: Kiểu dữ liệu cơ bản trong TypeScript

Bài 10.3: Kiểu dữ liệu cơ bản trong TypeScript
Chào mừng trở lại với series Lập trình Web Front-end! Sau khi đã làm quen với bức tranh tổng thể về TypeScript và lý do tại sao nó lại là một công cụ mạnh mẽ cho phát triển Front-end hiện đại, bước tiếp theo cực kỳ quan trọng là làm chủ các kiểu dữ liệu cơ bản.
Nếu bạn đã quen với JavaScript, bạn biết rằng nó rất linh hoạt (đôi khi quá linh hoạt!). Một biến có thể chứa một số, rồi sau đó lại chứa một chuỗi mà không gặp vấn đề gì. TypeScript mang đến sự có cấu trúc và dự đoán được bằng cách cho phép (hoặc khuyến khích) chúng ta định nghĩa rõ ràng kiểu dữ liệu mà một biến, tham số hàm, hoặc giá trị trả về sẽ chứa. Điều này không chỉ giúp TypeScript Compiler bắt lỗi cho bạn ngay trong quá trình viết code (trước khi code chạy!), mà còn làm cho code của bạn minh bạch, dễ đọc và dễ bảo trì hơn rất nhiều.
Hãy cùng khám phá những "viên gạch" nền tảng này của TypeScript nhé!
1. Kiểu Dữ liệu number
Kiểu number
trong TypeScript tương tự như trong JavaScript, được dùng để biểu diễn tất cả các giá trị số. Nó bao gồm cả số nguyên (integers) và số thực (floating-point numbers).
Cách khai báo một biến với kiểu number
:
let tuoi: number = 30; // Khai báo biến 'tuoi' kiểu number, gán giá trị nguyên
let giaTien: number = 19.99; // Khai báo biến 'giaTien' kiểu number, gán giá trị thực
let tong: number = tuoi + giaTien; // Các phép toán số học cho kết quả là number
console.log(tuoi); // Output: 30
console.log(giaTien); // Output: 19.99
console.log(tong); // Output: 49.99
Giải thích: Ở đây, chúng ta sử dụng dấu hai chấm :
sau tên biến (tuoi
, giaTien
, tong
) để chỉ định kiểu dữ liệu của nó là number
. TypeScript sau đó sẽ đảm bảo rằng bạn chỉ có thể gán các giá trị số cho các biến này. Nếu bạn cố gắng gán một giá trị không phải số (ví dụ: một chuỗi) cho biến tuoi
, TypeScript sẽ báo lỗi ngay khi biên dịch.
2. Kiểu Dữ liệu string
Kiểu string
được sử dụng để lưu trữ dữ liệu văn bản, tức là các chuỗi ký tự. TypeScript hỗ trợ cả ba cách khai báo chuỗi mà bạn đã quen thuộc từ JavaScript: dùng dấu ngoặc kép ("
), dấu ngoặc đơn ('
), và dấu backtick (```) cho template literals.
Cách khai báo một biến với kiểu string
:
let ten: string = "Alice"; // Dùng dấu ngoặc kép
let loiChao: string = 'Xin chào!'; // Dùng dấu ngoặc đơn
// Template literal (sử dụng dấu backtick `) cho phép nhúng biến/biểu thức vào chuỗi
let hoTen: string = "Bob Nguyen";
let thongBao: string = `Tên người dùng là: ${hoTen}`;
console.log(ten); // Output: Alice
console.log(loiChao); // Output: Xin chào!
console.log(thongBao); // Output: Tên người dùng là: Bob Nguyen
Giải thích: Biến ten
, loiChao
, và thongBao
được khai báo với kiểu string
. Bạn có thể sử dụng bất kỳ loại dấu ngoặc nào để định nghĩa chuỗi. Template literals với dấu backtick (`) và cú pháp
${tênBiến}` rất hữu ích khi bạn muốn tạo chuỗi động chứa giá trị của biến.
3. Kiểu Dữ liệu boolean
Kiểu boolean
là kiểu đơn giản nhất, chỉ có hai giá trị có thể có: true
(đúng) hoặc false
(sai). Nó thường được dùng để biểu diễn trạng thái bật/tắt, có/không, hoặc kết quả của các phép so sánh logic.
Cách khai báo một biến với kiểu boolean
:
let daDangNhap: boolean = true; // Biến 'daDangNhap' kiểu boolean, gán giá trị true
let coLoi: boolean = false; // Biến 'coLoi' kiểu boolean, gán giá trị false
let isGreaterThanTen: boolean = (15 > 10); // Kết quả của phép so sánh là boolean
console.log(daDangNhap); // Output: true
console.log(coLoi); // Output: false
console.log(isGreaterThanTen); // Output: true
Giải thích: Biến daDangNhap
và coLoi
rõ ràng thuộc kiểu boolean
. Biến isGreaterThanTen
nhận giá trị từ kết quả của biểu thức so sánh (15 > 10)
, mà kết quả này luôn là một giá trị boolean (true
hoặc false
).
4. Kiểu Dữ liệu null
và undefined
Đây là hai kiểu dữ liệu đặc biệt trong JavaScript và cả TypeScript, dùng để biểu thị sự vắng mặt của giá trị.
undefined
: Một biến có giá trịundefined
khi nó đã được khai báo nhưng chưa được gán bất kỳ giá trị nào.null
:null
biểu thị sự vắng mặt có chủ đích của giá trị. Tức là, bạn cố ý gánnull
cho một biến để nói rằng biến đó hiện không chứa một giá trị hợp lệ nào.
Lưu ý quan trọng: Trong chế độ strict
của TypeScript (rất nên dùng, được bật mặc định trong các dự án mới), null
và undefined
là hai kiểu riêng biệt và không thể gán cho nhau hoặc cho các kiểu dữ liệu khác như number
, string
, boolean
trừ khi bạn chỉ định rõ ràng (ví dụ: sử dụng Union Types như string | null
). Điều này giúp phát hiện lỗi sớm hơn rất nhiều.
Ví dụ:
let bienChuaGanGiaTri; // Khai báo biến nhưng chưa gán
console.log(bienChuaGanGiaTri); // Output: undefined (Đây là mặc định)
let duLieuNguoiDung: string | null = null; // Khai báo có thể là string hoặc null, ban đầu là null
console.log(duLieuNguoiDung); // Output: null
duLieuNguoiDung = "Chi tiết người dùng..."; // Sau đó có thể gán một giá trị string hợp lệ
console.log(duLieuNguoiDung); // Output: Chi tiết người dùng...
// Nếu strict mode bật, dòng dưới sẽ báo lỗi
// let someString: string = null; // Lỗi: Type 'null' is not assignable to type 'string'.
Giải thích: Khi bienChuaGanGiaTri
được khai báo mà không gán giá trị ban đầu, nó tự động có giá trị và kiểu là undefined
. Biến duLieuNguoiDung
được khai báo với kiểu là Union Type string | null
, cho phép nó nhận giá trị là một chuỗi hoặc null
. Điều này là cách làm chuẩn trong TypeScript khi bạn muốn một biến có thể "không có giá trị" một cách rõ ràng.
5. Kiểu Dữ liệu void
Kiểu void
thường được dùng làm kiểu trả về của các hàm mà không trả về bất kỳ giá trị nào. Điều này phổ biến với các hàm chỉ thực hiện một hành động nào đó, ví dụ như ghi log ra console, thay đổi trạng thái, v.v.
Ví dụ:
function inThongDiep(thongDiep: string): void {
console.log(thongDiep);
// Hàm này không có lệnh return giá trị nào
}
inThongDiep("Hoàn thành tác vụ!"); // Output: Hoàn thành tác vụ!
let ketQua = inThongDiep("Một thông điệp khác"); // ketQua sẽ có kiểu là void (hay undefined trong JS runtime)
console.log(ketQua); // Output: Một thông điệp khác (in ra từ hàm)
// Output: undefined (giá trị của biến ketQua)
Giải thích: Hàm inThongDiep
được khai báo với kiểu trả về là : void
. Điều này nói cho TypeScript biết rằng hàm này sẽ không return
một giá trị có ý nghĩa nào cả. Khi gọi hàm này và gán kết quả cho biến ketQua
, biến này sẽ nhận giá trị undefined
(đây là cách JavaScript xử lý việc "không trả về gì"), và TypeScript hiểu điều đó thông qua kiểu void
.
6. Kiểu Dữ liệu any
(Hãy Cẩn Thận!)
Kiểu any
là kiểu dữ liệu cho phép bạn gán bất kỳ giá trị nào thuộc bất kỳ kiểu dữ liệu nào cho biến. Khi một biến có kiểu any
, TypeScript sẽ không kiểm tra kiểu cho biến đó. Nó giống như bạn đang tạm thời quay về thế giới của JavaScript thuần vậy.
Cách khai báo một biến với kiểu any
:
let duLieuDong: any = 10; // Ban đầu gán giá trị number
console.log(duLieuDong); // Output: 10
duLieuDong = "Bây giờ gán giá trị string"; // Gán lại giá trị string
console.log(duLieuDong); // Output: Bây giờ gán giá trị string
duLieuDong = { ten: "Anna" }; // Gán lại giá trị object
console.log(duLieuDong); // Output: { ten: 'Anna' }
// TypeScript sẽ không báo lỗi ở đây, dù có thể gây lỗi lúc chạy
// duLieuDong.toUpperCase(); // Lỗi lúc chạy vì duLieuDong hiện là object!
Giải thích: Biến duLieuDong
với kiểu any
cho phép chúng ta gán lại các giá trị thuộc các kiểu khác nhau (number
, string
, object
) mà không bị TypeScript báo lỗi trong quá trình biên dịch.
Tại sao lại "Hãy Cẩn Thận!"?
any
hữu ích trong các trường hợp cần tương tác với code JavaScript cũ chưa được viết lại bằng TypeScript, hoặc khi bạn đang xử lý dữ liệu có cấu trúc rất động và không thể biết trước kiểu của nó một cách dễ dàng.
Tuy nhiên, lạm dụng any
làm mất đi hầu hết lợi ích của TypeScript về kiểm tra kiểu tĩnh. Nó làm giảm khả năng bắt lỗi sớm và giảm tính minh bạch của code. Hãy cố gắng sử dụng các kiểu cụ thể, hoặc các kiểu linh hoạt hơn nhưng vẫn an toàn như unknown
(sẽ học sau) bất cứ khi nào có thể thay vì any
.
Một số kiểu cơ bản khác
Ngoài các kiểu phổ biến trên, TypeScript còn hỗ trợ một số kiểu cơ bản khác ít gặp hơn trong các tình huống thông thường nhưng vẫn quan trọng:
symbol
: Dùng để tạo ra các định danh duy nhất và bất biến, thường được sử dụng làm khóa cho các thuộc tính của object để tránh xung đột tên.const idMoi: symbol = Symbol('user-id'); let nguoiDung = { [idMoi]: 123, // Sử dụng symbol làm khóa thuộc tính ten: "Charlie" }; console.log(nguoiDung[idMoi]); // Output: 123 // console.log(nguoiDung.idMoi); // Lỗi! Không thể truy cập bằng tên chuỗi thông thường
bigint
: Dùng để biểu diễn các số nguyên rất lớn vượt quá giới hạn an toàn của kiểunumber
thông thường (làNumber.MAX_SAFE_INTEGER
, khoảng 2^53).let soNguyenLon: bigint = 9007199254740991n; // Khai báo với 'n' ở cuối let soLonHon: bigint = soNguyenLon + 1n; // Các phép toán cần thực hiện trên bigint console.log(soLonHon); // Output: 9007199254740992n
Các kiểu này ít được sử dụng trong các bài toán Front-end cơ bản, nhưng rất hữu ích khi làm việc với các API hoặc dữ liệu cần độ chính xác cao cho các số nguyên lớn.
TypeScript Rất "Thông Minh": Suy Luận Kiểu (Type Inference)
Một điểm tuyệt vời giúp bạn viết code TypeScript gọn gàng hơn là bạn không luôn luôn phải viết rõ kiểu dữ liệu. Trong nhiều trường hợp, TypeScript có thể tự động suy luận ra kiểu dựa trên giá trị bạn gán cho biến khi khai báo.
Ví dụ:
let tenSanPham = "Laptop"; // TypeScript suy luận tenSanPham là kiểu string
// tenSanPham = 12345; // Lỗi! Type 'number' is not assignable to type 'string'.
let soLuongTonKho = 150; // TypeScript suy luận soLuongTonKho là kiểu number
// soLuongTonKho = "hết hàng"; // Lỗi! Type 'string' is not assignable to type 'number'.
let dangHoatDong = true; // TypeScript suy luận dangHoatDong là kiểu boolean
Giải thích: Khi bạn khai báo một biến và gán ngay một giá trị ban đầu, TypeScript đủ thông minh để biết kiểu dữ liệu của giá trị đó và áp dụng kiểu đó cho biến. Bạn vẫn nhận được lợi ích kiểm tra kiểu mà không cần viết thêm :
và tên kiểu. Tuy nhiên, việc viết rõ kiểu dữ liệu (type annotation) là cần thiết và khuyến khích trong các trường hợp phức tạp hơn hoặc khi khai báo biến mà chưa gán giá trị ban đầu.
Nắm vững các kiểu dữ liệu cơ bản này là bước đệm vững chắc để bạn tiếp tục khám phá những tính năng mạnh mẽ hơn, phức tạp hơn của TypeScript như Union Types, Intersection Types, Arrays, Tuples, Enums, Interfaces, v.v. Hãy luyện tập sử dụng chúng thường xuyên để làm quen nhé!
Comments