NỘI DUNG BÀI HỌC
🚀TypeScript là gì? Tại sao nên dùng TypeScript thay vì JavaScript thuần?
🚀Làm quen với các kiểu dữ liệu cơ bản (Primitive Types) trong TypeScript.
🚀Cách định kiểu dữ liệu cho Mảng (Array) và Đối tượng (Object).
🚀Hướng dẫn cài đặt môi trường Node.js, npm và TypeScript từ A-Z.
Bạn đã bao giờ viết code JavaScript và gặp phải những lỗi "trời ơi đất hỡi" như undefined is not a function, truyền sai loại dữ liệu cho một hàm, hay gõ nhầm tên một thuộc tính và phải mất hàng giờ để tìm ra chưa?
Nếu câu trả lời là có, thì TypeScript chính là "vị cứu tinh" của bạn.
Phần 1: TypeScript là gì?
Định nghĩa: TypeScript là một superset (tập hợp cha) của JavaScript, được phát triển bởi Microsoft.
Điều này có nghĩa là:
- Tất cả code JavaScript hợp lệ cũng là code TypeScript hợp lệ. Bạn không cần phải "học lại từ đầu".
- TypeScript chỉ đơn giản là thêm các tính năng mới vào trên nền tảng JavaScript mà bạn đã biết.
Ví von: 🧠 Hãy tưởng tượng JavaScript là một Ninja 🥷 - cực kỳ linh hoạt, nhanh nhẹn nhưng đôi khi hơi "liều lĩnh". TypeScript chính là bộ giáp công nghệ cao 🦾 mà chúng ta trang bị cho Ninja đó.
- Ninja vẫn có thể làm mọi thứ như trước.
- Nhưng bộ giáp sẽ bảo vệ anh ta khỏi những sai lầm phổ biến và cung cấp các công cụ hỗ trợ "xịn" hơn.
Bộ giáp đó chính là hệ thống kiểu dữ liệu (Type System).
Phần 2: Vậy "Kiểu" (Type) là gì và nó giúp ích ra sao?
- Vấn đề của JavaScript (Kiểu dữ liệu động)
Như chúng ta đã học, JavaScript rất linh hoạt. Một biến có thể là số, rồi sau đó lại trở thành chuỗi.
let soLuong = 5; // Hiện tại là number
soLuong = "Năm sản phẩm"; // Giờ lại là string
// JavaScript hoàn toàn cho phép điều này!
Sự linh hoạt này tiện lợi nhưng cũng là nguồn gốc của vô số lỗi, vì bạn có thể vô tình gán sai loại dữ liệu và chỉ phát hiện ra khi chương trình đã chạy và bị crash.
Giải pháp của TypeScript (Kiểu dữ liệu tĩnh)
TypeScript cho phép (và khuyến khích) bạn "chú thích kiểu" (type annotation) cho các biến, tham số hàm, và giá trị trả về.
let soLuong: number = 5; // Khai báo: biến này PHẢI là một number
// Dòng này sẽ bị gạch chân đỏ và BÁO LỖI ngay lập tức trong trình soạn thảo code!
soLuong = "Năm sản phẩm"; // Lỗi: Type 'string' is not assignable to type 'number'.
Đây là sức mạnh cốt lõi: TypeScript hoạt động như một "trợ lý ảo", kiểm tra code của bạn ngay khi bạn đang gõ và phát hiện các lỗi logic về kiểu dữ liệu trước cả khi bạn chạy kịch bản test.
Tại sao Automation Tester lại "Mê mẩn" TypeScript?
Đối với automation, sự ổn định và đáng tin cậy của kịch bản test là ưu tiên hàng đầu. TypeScript cung cấp chính xác điều đó.
- Phát hiện lỗi sớm (Catch Errors Early) 🔍
- JavaScript: Bạn viết sai logic, chạy test, chờ 30 giây, test thất bại, sau đó bạn mới đi tìm lỗi.
- TypeScript: Bạn viết sai logic, trình soạn thảo code (như VS Code) báo lỗi ngay lập tức.
Ví von: 🧠 Nó giống như một cái chuông báo khói kêu lên ngay khi bạn làm cháy đồ ăn, thay vì chờ đến khi cả ngôi nhà bốc cháy rồi cứu hỏa mới đến.
- Code Tự-gợi-ý "Thần sầu" (Intelligent Code Completion) ✨
Vì TypeScript biết chính xác kiểu dữ liệu của mọi thứ, nó cung cấp gợi ý code cực kỳ chính xác.
// Giả sử bạn có một class LoginPage
const loginPage = new LoginPage(page);
// Khi bạn gõ "loginPage."
// VS Code sẽ hiện ra một danh sách chính xác các phương thức:
// login()
// goTo()
// getErrorMessage()
Điều này giúp bạn viết code nhanh hơn, giảm thiểu lỗi gõ nhầm và không cần phải nhớ hết tên các phương thức.
Dễ đọc và Dễ bảo trì (Readability & Maintainability) 📖
Code TypeScript tự nó đã là một tài liệu.
- Code JavaScript: function login(user, pass) { ... } (user là string hay object?)
- Code TypeScript: function login(user: string, pass: string): void { ... } (Rất rõ ràng: user là string, pass là string, hàm này không trả về gì cả).
Khi làm việc trong một team hoặc xem lại code của mình sau 6 tháng, bạn sẽ biết ơn TypeScript vì sự rõ ràng này.
Tái cấu trúc An toàn (Safe Refactoring) 🛠️
Khi bạn đổi tên một phương thức trong class LoginPage, TypeScript sẽ ngay lập tức tìm và báo lỗi ở tất cả những nơi đang gọi đến tên phương thức cũ. Điều này đảm bảo rằng bạn sẽ không bỏ sót bất kỳ chỗ nào, giúp việc nâng cấp và bảo trì framework trở nên cực kỳ an toàn.
Kết luận: TypeScript không thay thế JavaScript, nó nâng cấp JavaScript. Việc bỏ ra một chút công sức ban đầu để định nghĩa kiểu sẽ tiết kiệm cho bạn hàng giờ, thậm chí hàng ngày, trong việc gỡ lỗi và bảo trì các kịch bản test sau này.
Phần 3: Chú thích kiểu dữ liệu cơ bản
Cú pháp cơ bản: Chú thích Kiểu
Trong TypeScript, bạn sử dụng dấu hai chấm (:) theo sau là tên kiểu dữ liệu để khai báo kiểu cho một biến.
let ten_bien: kieu_du_lieu = gia_tri;
Ví von: 🧠 Cú pháp này giống như bạn lấy một cái nhãn và dán lên một cái hộp trước khi bỏ đồ vào. Một cái hộp đã được dán nhãn "Táo" 🍎 thì bạn không thể bỏ "Chuối" 🍌 vào được.
Các Kiểu dữ liệu Nguyên thủy (Primitive Types)
Đây là những kiểu dữ liệu cơ bản nhất mà bạn đã quen thuộc từ JavaScript.
string (Chuỗi)
Dùng để lưu trữ dữ liệu văn bản.
let browserName: string;
browserName = "chrome"; // OK
// browserName = 123; // LỖI! Type 'number' is not assignable to type 'string'.
number (Số)
Dùng để lưu trữ các giá trị số, bao gồm cả số nguyên và số thập phân.
let timeout: number = 30000;
timeout = 45000; // OK
// timeout = "30s"; // LỖI! Type 'string' is not assignable to type 'number'.
boolean (Luận lý)
Dùng để lưu trữ giá trị true hoặc false.
let isHeadless: boolean = true
isHeadless = false; // OK
// isHeadless = 1; // LỖI! Type 'number' is not assignable to type 'boolean'.
Các Kiểu Đặc biệt
any - "Cánh cửa thoát hiểm" 🚪
- Công dụng: any là một kiểu đặc biệt cho phép bạn gán bất kỳ loại giá trị nào cho một biến. Về cơ bản, nó ra lệnh cho TypeScript: "Đừng kiểm tra kiểu dữ liệu cho biến này, hãy để nó hoạt động như JavaScript thông thường."
- Ví von: 🧠 Nó giống như một cái hộp không dán nhãn. Bạn có thể bỏ bất cứ thứ gì vào đó.
- Ví dụ:
let duLieuBatKy: any = "Đây là một chuỗi";
duLieuBatKy = 123; // OK
duLieuBatKy = true; // OK
// Nguy hiểm: TypeScript sẽ không báo lỗi, nhưng code sẽ sập lúc chạy!
// duLieuBatKy.toUpperCase();
- ⚠️ Cảnh báo: Hãy hạn chế sử dụng any tối đa có thể. Nó là một "cánh cửa thoát hiểm" khi bạn làm việc với code JavaScript cũ hoặc các thư viện không có kiểu, không phải là công cụ để sử dụng hàng ngày.
unknown - Phiên bản an toàn của any 🧐
- Công dụng: Giống như any, bạn có thể gán bất kỳ giá trị nào cho một biến kiểu unknown.
- Sự khác biệt: Trước khi bạn có thể sử dụng biến unknown, TypeScript bắt buộc bạn phải thực hiện một phép kiểm tra để xác định rõ kiểu dữ liệu của nó là gì.
- Ví von: 🧠 Nó là một "chiếc hộp bí ẩn". Bạn biết có đồ bên trong, nhưng bạn không được phép dùng cho đến khi bạn "mở hộp ra kiểm tra" (dùng typeof, instanceof...).
- Ví dụ:
let giaTriBiAn: unknown = "Xin chào từ API";
// Dòng này sẽ BÁO LỖI ngay lập tức
// console.log(giaTriBiAn.length);
// Phải kiểm tra trước khi dùng
if (typeof giaTriBiAn === 'string') {
// OK! Bên trong khối if này, TypeScript biết chắc nó là string.
console.log(giaTriBiAn.length);
}
void, null, và undefined
- void: Chủ yếu được dùng để chú thích cho một hàm không trả về giá trị nào cả.
function logMessage(message: string): void {
console.log(message);
// Không có lệnh 'return'
}
- null & undefined: Chúng là các kiểu dữ liệu riêng. Trong thực tế, bạn sẽ thường thấy chúng được kết hợp với các kiểu khác (sẽ học ở bài Union Types).
let username: string | null = "tester";
username = null; // OK
Type Inference - Sự "suy luận" thông minh của TypeScript 🤖
Bạn không phải lúc nào cũng cần viết chú thích kiểu. Nếu bạn khai báo và gán giá trị cho một biến ngay trên cùng một dòng, TypeScript đủ thông minh để tự suy luận (infer) ra kiểu dữ liệu của biến đó.
- Ví von: 🧠 Nếu bạn bỏ một quả táo vào một cái hộp không nhãn, TypeScript sẽ nhìn vào bên trong, thấy quả táo và tự dán nhãn "Táo" lên cái hộp đó giúp bạn.
- Ví dụ:
// Bạn viết:
let tenDuAn = "Playwright Project";
// TypeScript tự suy luận ngầm hiểu là:
// let tenDuAn: string = "Playwright Project";
// Do đó, dòng sau sẽ báo lỗi dù bạn không hề ghi ': string'
// tenDuAn = 123;
Lời khuyên: Hãy tận dụng Type Inference cho các biến đơn giản để code gọn gàng hơn. Tuy nhiên, hãy luôn chú thích kiểu tường minh cho các tham số của hàm và giá trị trả về để đảm bảo code rõ ràng.
Phần 4: Chú thích kiểu cho mảng và object
Mảng (Arrays)
Mảng trong TypeScript vẫn là một danh sách các phần tử, nhưng sức mạnh của TypeScript là nó cho phép chúng ta bắt buộc tất cả các phần tử trong mảng phải có cùng một kiểu dữ liệu.
Ví von: 🧠 Thay vì một "cái giỏ đa năng", bây giờ bạn có những "cái giỏ chuyên dụng": một giỏ chỉ để đựng Táo 🍎 (string[]), một giỏ chỉ để đựng Cam 🍊 (number[]).
Cú pháp
Có hai cách viết phổ biến:
kieu_du_lieu[] (Phổ biến nhất):
// Một mảng chỉ chứa các chuỗi
let browserList: string[] = ["chrome", "firefox", "webkit"];
Array<kieu_du_lieu> (Generic syntax):
// Một mảng chỉ chứa các số
let retryAttempts: Array<number> = [1, 2, 3];
Ứng dụng trong Automation
Việc định kiểu cho mảng giúp đảm bảo dữ liệu test của bạn luôn nhất quán.
let supportedEnvironments: string[] = ["staging", "production"];
supportedEnvironments.push("uat"); // OK
// TypeScript sẽ BÁO LỖI ngay lập tức nếu bạn cố thêm sai kiểu dữ liệu!
// supportedEnvironments.push(123);
// Lỗi: Argument of type 'number' is not assignable to parameter of type 'string'.
Đối tượng (Objects)
Đây là phần cực kỳ quan trọng. TypeScript cho phép bạn định nghĩa "hình dạng" (shape) của một object, quy định rõ nó phải có những thuộc tính (key) nào và kiểu dữ liệu của từng thuộc tính là gì.
Chú thích kiểu trực tiếp
Bạn có thể định nghĩa cấu trúc của object ngay sau tên biến.
let testUser: {
username: string;
id: number;
isVip: boolean;
};
// Gán một object khớp với cấu trúc
testUser = {
username: "standard_user",
id: 101,
isVip: false
}; // OK
// TypeScript sẽ báo lỗi nếu:
// 1. Thiếu thuộc tính
// testUser = { username: "guest", id: 102 }; // Lỗi: Property 'isVip' is missing.
// 2. Sai kiểu dữ liệu
// testUser = { username: "admin", id: "001", isVip: true }; // Lỗi: Type 'string' is not assignable to type 'number'.
Thuộc tính Tùy chọn (?)
Đặt dấu ? sau tên thuộc tính để cho biết thuộc tính đó có thể có hoặc không.
Ứng dụng trong Automation: 🧪 Rất hữu ích cho các dữ liệu test mà một số trường không bắt buộc.
let testConfig: {
baseURL: string;
timeout: number;
tags?: string[]; // 'tags' là một mảng chuỗi tùy chọn
};
// Object này hợp lệ dù không có 'tags'
testConfig = {
baseURL: "https://my-app.com",
timeout: 5000
};
Thuộc tính Chỉ đọc (readonly)
Đặt từ khóa readonly trước tên thuộc tính để ngăn không cho nó bị gán lại giá trị sau khi đã được khởi tạo.
Ứng dụng trong Automation: 🧪 Hoàn hảo cho các dữ liệu không bao giờ nên thay đổi trong quá trình test, như ID của user hay mã sản phẩm.
let product: {
readonly sku: string; // Stock Keeping Unit
name: string;
price: number;
};
product = {
sku: "IPHONE15-PRO-256",
name: "iPhone 15 Pro",
price: 25000000
};
// product.sku = "NEW-SKU"; // LỖI! Cannot assign to 'sku' because it is a read-only property.
Phần 5: Cài đặt npm và typescript
Node.js và npm là gì?
Trước hết, hãy hiểu vai trò của chúng:
Node.js: Là một môi trường để bạn có thể chạy code JavaScript ở bên ngoài trình duyệt web (ví dụ: chạy trên máy chủ, máy tính cá nhân).
npm (Node Package Manager): Là trình quản lý gói đi kèm với Node.js. Hãy coi nó như một "kho ứng dụng" khổng lồ, nơi các lập trình viên chia sẻ những đoạn code (gọi là package hoặc module) để người khác có thể tái sử dụng.
Tin vui là khi bạn cài đặt Node.js, npm sẽ được cài đặt tự động.
Hướng dẫn cài đặt Node.js và npm
Bước 1: Tải về bộ cài đặt
Truy cập trang web chính thức của Node.js: https://nodejs.org/
Bạn sẽ thấy hai phiên bản để tải về:
LTS (Long-Term Support): Đây là phiên bản ổn định nhất, được hỗ trợ lâu dài. Bạn nên chọn phiên bản này nếu mới bắt đầu hoặc làm dự án thực tế.
Current: Phiên bản mới nhất, chứa các tính năng thử nghiệm. Chỉ nên dùng nếu bạn muốn trải nghiệm những thứ mới nhất.
Bước 2: Chạy file cài đặt
Sau khi tải về, hãy mở file và thực hiện các bước cài đặt. Quá trình này rất đơn giản, bạn chỉ cần nhấn "Next" liên tục và chấp nhận các điều khoản, cuối cùng nhấn "Install" và "Finish".
Bước 3: Kiểm tra xem đã cài đặt thành công chưa
Đây là bước quan trọng nhất. Mở Terminal của bạn (trên macOS/Linux) hoặc Command Prompt/PowerShell (trên Windows) và gõ lần lượt hai lệnh sau:
Kiểm tra phiên bản Node.js:
node -v
Kiểm tra phiên bản npm:
npm -v
Nếu cả hai lệnh đều trả về một số phiên bản (ví dụ: v20.11.0 và 10.2.4), xin chúc mừng, bạn đã cài đặt thành công! 🎉
Quản lý gói (Package) và dự án
Mọi dự án Node.js đều bắt đầu với một file cấu hình tên là package.json.
package.json - "Chứng minh nhân dân" của dự án
File này chứa các thông tin quan trọng về dự án của bạn:
Tên dự án, phiên bản, mô tả...
Quan trọng nhất: Danh sách các package mà dự án của bạn cần để chạy (gọi là dependencies).
Để tạo file này, bạn chỉ cần:
Tạo một thư mục mới cho dự án.
Mở terminal trong thư mục đó.
Chạy lệnh:
npm init -y
Lệnh này sẽ tự động tạo ra một file package.json với các thông tin mặc định.
Ví dụ thực hành - Cài đặt và sử dụng một package
Bây giờ, chúng ta sẽ thực hành toàn bộ quy trình bằng cách cài đặt và sử dụng lodash, một package rất phổ biến với nhiều hàm tiện ích.
Bước 1: Tạo dự án
Mở terminal và chạy các lệnh sau để tạo một thư mục mới và di chuyển vào đó:
mkdir my-first-app
cd my-first-app
Bước 2: Khởi tạo dự án
Chạy lệnh npm init để tạo file package.json:
npm init -y
Bước 3: Cài đặt package lodash
Bây giờ, chúng ta sẽ yêu cầu npm tải lodash từ trên mạng về và lưu vào dự án:
npm install lodash
Sau khi lệnh này chạy xong, bạn sẽ thấy 3 thay đổi lớn:
Một thư mục mới tên là node_modules được tạo ra. Đây là nơi chứa code của lodash và các package khác mà bạn cài.
Một file mới tên là package-lock.json được tạo ra. File này ghi lại chính xác phiên bản của các package đã cài.
File package.json của bạn được cập nhật, thêm lodash vào mục "dependencies".
Bước 4: Viết code để sử dụng package
Tạo một file mới tên là index.js trong cùng thư mục dự án và viết đoạn code sau vào:
// Gọi (import) thư viện lodash vào để sử dụng
const _ = require('lodash');
// Tạo một mảng các con số
const numbers = [10, 20, 30, 40, 50];
// Dùng hàm shuffle() của lodash để xáo trộn mảng
const shuffledNumbers = _.shuffle(numbers);
// In kết quả ra màn hình
console.log("Mảng ban đầu:", numbers);
console.log("Mảng sau khi xáo trộn:", shuffledNumbers);
Bước 5: Chạy chương trình!
Quay lại terminal (bạn vẫn đang ở trong thư mục my-first-app), chạy file index.js bằng Node.js:
node index.js
Bạn sẽ thấy kết quả tương tự như sau (thứ tự các số sẽ ngẫu nhiên mỗi lần chạy):
Mảng ban đầu: [ 10, 20, 30, 40, 50 ]
Mảng sau khi xáo trộn: [ 40, 10, 50, 20, 30 ]
Cài đặt TypeScript và ts-node
Chạy lệnh cài đặt:
npm install typescript ts-node --save-dev
typescript: Gói này cung cấp trình biên dịch (tsc) để "dịch" code TypeScript.
ts-node: Gói này giúp chạy file TypeScript trực tiếp mà không cần biên dịch ra file JavaScript trung gian.
--save-dev: Lệnh này lưu cả hai gói vào mục "devDependencies" trong package.json, vì chúng chỉ là công cụ hỗ trợ lúc phát triển.
(Khuyến khích) Tạo file cấu hình TypeScript:
Để ts-node và các công cụ khác biết cách biên dịch code của bạn, hãy tạo file tsconfig.json. Chạy lệnh sau:
npx tsc --init
Lệnh này sẽ tạo ra một file tsconfig.json với rất nhiều tùy chọn. Hiện tại bạn chưa cần chỉnh sửa gì cả.
Chạy file ts sử dụng:
npx ts-node index.ts
npx: Đây là một công cụ đi kèm với npm, giúp bạn chạy các gói được cài đặt cục bộ trong thư mục node_modules của dự án.