Lập trình · Ngôn ngữ lập trình
Hướng dẫn học TypeScript cơ bản cho người mới
Trả lời nhanh
TypeScript là JavaScript có kiểu tĩnh. Bạn thêm chú thích kiểu như let count: number = 0, và trình biên dịch tsc kiểm tra chúng trước khi chuyển mã thành JavaScript thuần chạy được trên mọi trình duyệt hoặc Node.js. Các ý tưởng chính là kiểu cơ bản, interface và type alias cho hình dạng đối tượng, union type với narrowing, và generic để tái sử dụng mã. Kiểu bị xóa khi chạy.
TypeScript là gì và dùng để làm gì
TypeScript là ngôn ngữ mã nguồn mở do Microsoft phát triển, phát hành lần đầu năm 2012. Mọi chương trình JavaScript hợp lệ cũng là TypeScript hợp lệ, nên bạn có thể áp dụng dần từng tệp. Bộ kiểm tra kiểu bắt các lỗi như viết sai tên thuộc tính hoặc thiếu kiểm tra null ngay khi bạn gõ, và trình soạn thảo dùng kiểu để tự động hoàn thành và đổi tên an toàn. Nó được dùng rộng rãi cho ứng dụng front end lớn (Angular được viết bằng nó, còn React, Vue và Svelte hỗ trợ rất tốt), back end Node.js và thư viện.
Cài đặt và chạy Hello, world
Hãy cài Node.js trước, rồi cài trình biên dịch vào dự án bằng npm. tsc biên dịch tệp .ts thành .js.
const message: string = "Hello, world";
console.log(message);npm install --save-dev typescript
npx tsc --init # creates tsconfig.json
npx tsc hello.ts # writes hello.js
node hello.js
# Hello, worldCác phiên bản Node.js, Deno và Bun gần đây cũng có thể chạy trực tiếp tệp .ts bằng cách bỏ chú thích kiểu. Chúng không kiểm tra kiểu, vì vậy vẫn hãy chạy npx tsc --noEmit để tìm lỗi kiểu.
Kiểu cơ bản và suy luận kiểu
Bạn hiếm khi cần viết mọi kiểu. Khi khởi tạo biến, TypeScript suy luận kiểu của nó: let n = 5 là number. Hãy chú thích tham số hàm và API công khai.
| Kiểu | Ví dụ | Ghi chú |
|---|---|---|
number | let n: number = 3.5 | Số nguyên và số thực |
string | let s: string = "hi" | Văn bản |
boolean | let ok: boolean = true | true hoặc false |
| Mảng | number[] hoặc Array<number> | Hai cách viết có cùng ý nghĩa |
| Tuple | [string, number] | Độ dài và vị trí cố định |
unknown | let x: unknown | Giá trị bất kỳ, phải kiểm tra trước khi dùng |
any | let y: any | Tắt kiểm tra; nên tránh |
void / never | kiểu trả về của hàm | Không trả về gì / không bao giờ trả về |
let count = 0; // inferred as number
count = "ten"; // error: Type 'string' is not assignable to type 'number'
const names: string[] = ["Ann", "Bo"];
const pair: [string, number] = ["age", 31];Dòng thứ hai là lỗi biên dịch được cố ý đưa vào để minh họa những gì bộ kiểm tra báo.
Hàm, điều khiển luồng và thu hẹp kiểu
Điều khiển luồng (if, for, while, switch) giống hệt JavaScript. Điều TypeScript bổ sung là narrowing (thu hẹp kiểu): sau một phép kiểm tra như typeof x === "string", trình biên dịch biết kiểu cụ thể hơn bên trong nhánh đó.
function greet(name: string, greeting = "Hello"): string {
return `${greeting}, ${name}!`;
}
function formatId(id: string | number): string {
if (typeof id === "number") {
return id.toFixed(0).padStart(6, "0"); // id is number here
}
return id.toUpperCase(); // id is string here
}
console.log(greet("Lin")); // Hello, Lin!
console.log(formatId(42)); // 000042
console.log(formatId("ab7")); // AB7string | number là union type: giá trị thuộc một trong các kiểu được liệt kê. Tham số tùy chọn được viết name?: string, và kiểu của chúng bao gồm undefined.
Interface và type alias
Mô tả hình dạng đối tượng bằng interface hoặc bí danh type. TypeScript kiểm tra cấu trúc, không phải tên: bất kỳ đối tượng nào có đúng thuộc tính đều phù hợp.
interface User {
id: number;
name: string;
email?: string; // optional
readonly createdAt: Date;
}
type Role = "admin" | "editor" | "viewer"; // string literal union
function describe(user: User, role: Role): string {
const contact = user.email ?? "no email";
return `${user.name} (${role}, ${contact})`;
}
const ann: User = { id: 1, name: "Ann", createdAt: new Date() };
console.log(describe(ann, "editor")); // Ann (editor, no email)Tính năng đặc trưng: generic và discriminated union
Generic cho phép một hàm hoặc kiểu làm việc với nhiều kiểu mà vẫn giữ chúng liên kết với nhau. Discriminated union là union của các kiểu đối tượng có chung một trường literal, nên switch trên trường đó thu hẹp kiểu, và trình biên dịch có thể kiểm tra rằng mọi trường hợp đều được xử lý.
function first<T>(items: T[]): T | undefined {
return items[0];
}
const n = first([10, 20]); // n: number | undefined
const s = first(["a", "b"]); // s: string | undefined
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rect"; width: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rect":
return shape.width * shape.height;
}
}
console.log(n, s, area({ kind: "rect", width: 2, height: 3 })); // 10 a 6Các utility type có sẵn tạo kiểu mới từ kiểu cũ: Partial<User> biến mọi thuộc tính thành tùy chọn, Pick<User, "id" | "name"> chỉ giữ một số thuộc tính, và Record<string, number> mô tả một từ điển.
Xử lý lỗi
Lỗi hoạt động như trong JavaScript, với throw và try/catch. Khi bật tùy chọn strict, giá trị bắt được có kiểu unknown, vì bất kỳ giá trị nào cũng có thể bị ném, nên hãy kiểm tra trước khi dùng.
function parseAge(text: string): number {
const age = Number(text);
if (!Number.isInteger(age) || age < 0) {
throw new Error(`invalid age: ${text}`);
}
return age;
}
try {
parseAge("abc");
} catch (err: unknown) {
const message = err instanceof Error ? err.message : String(err);
console.log(message); // invalid age: abc
}Một chương trình nhỏ hoàn chỉnh: kho hàng có kiểu
interface Item {
name: string;
price: number;
qty: number;
}
type Summary = { total: number; cheapest?: Item; outOfStock: string[] };
function summarize(items: Item[]): Summary {
let total = 0;
let cheapest: Item | undefined;
const outOfStock: string[] = [];
for (const item of items) {
total += item.price * item.qty;
if (item.qty === 0) outOfStock.push(item.name);
if (!cheapest || item.price < cheapest.price) cheapest = item;
}
return { total, cheapest, outOfStock };
}
const result = summarize([
{ name: "pen", price: 1.5, qty: 10 },
{ name: "book", price: 12, qty: 2 },
{ name: "bag", price: 30, qty: 0 },
]);
console.log(result.total, result.cheapest?.name, result.outOfStock);
// 39 pen [ 'bag' ]Hãy thử đổi qty: 10 thành qty: "10": trình biên dịch báo lỗi trước cả khi mã chạy.
Những lỗi thường gặp của người mới
- Dùng
anyđể làm im lỗi. Nên dùngunknownrồi thu hẹp kiểu. - Lạm dụng ép kiểu
as, vốn bảo trình biên dịch tin bạn thay vì kiểm tra. - Để
stricttắt trongtsconfig.json. Hãy bật nó ở dự án mới; nó kích hoạt kiểm tranull. - Viết những kiểu mà trình biên dịch đã tự suy luận được, chỉ thêm rối mà không thêm an toàn.
Học tiếp ở đâu
Hãy đọc TypeScript Handbook chính thức, và thử nghiệm trong TypeScript Playground trên typescriptlang.org, nơi hiển thị JavaScript đã biên dịch và lỗi cạnh nhau. Sau đó học các tùy chọn tsconfig.json, tệp khai báo kiểu cho thư viện (các gói @types/...) và TypeScript với framework bạn chọn.
Câu hỏi thường gặp
TypeScript có phải ngôn ngữ khác với JavaScript không?
Nó là tập mở rộng (superset) của JavaScript: thêm cú pháp kiểu và bộ kiểm tra, rồi biên dịch thành JavaScript thuần. Hành vi khi chạy giống JavaScript mà nó sinh ra.
Có nên học JavaScript trước TypeScript không?
Hãy học nền tảng JavaScript trước (biến, hàm, đối tượng, promise), vì TypeScript không thay đổi chúng. Sau đó thêm TypeScript; kiểu dễ học hơn khi bạn đã quen hành vi lúc chạy.
interface và type khác nhau thế nào?
Cả hai đều mô tả hình dạng đối tượng. Interface có thể được mở rộng và gộp lại bằng cách khai báo lại; type alias còn có thể đặt tên cho union, tuple và các kiểu không phải đối tượng. Với hình dạng đối tượng thông thường, cái nào cũng được.
TypeScript có làm mã của tôi chạy nhanh hơn không?
Không. Kiểu bị loại bỏ khi biên dịch, nên kết quả chạy với tốc độ như JavaScript viết tay. Lợi ích là ít lỗi hơn và công cụ tốt hơn.