TypeScript không chỉ đơn thuần là công cụ để định kiểu cho mã nguồn — mà còn là một công cụ mạnh mẽ giúp bạn viết phần mềm an toàn hơn, biểu đạt rõ ràng hơn và dễ bảo trì hơn.
Dưới đây là 12 kỹ thuật nâng cao để tận dụng tối đa sức mạnh của TypeScript:
1. infer
: Trích xuất kiểu mà không lặp lại logic
Từ khóa infer
cho phép bạn bắt kiểu một cách linh hoạt trong các loại điều kiện — lý tưởng để suy diễn kiểu trả về của hàm mà không phải lặp lại logic.
type GetParserResult<T> = T extends | (() => infer TResult) | { parse: () => infer TResult } | { extract: () => infer TResult } ? TResult : never;
2. Kiểu literal với template string
Kiểu literal dạng template cho phép bạn kết hợp các giá trị một cách khai báo.
type Bread = "croissant" | "baguette";
type Filling = "cheese" | "ham";
type Option = `${Filling} ${Bread}`;
// "cheese croissant" | "ham baguette" | ...
Bạn thậm chí có thể tùy biến với emoji:
type Fruit = "apple" | "banana";
type FruitEmoji = { [K in Fruit]: `${K} 🍎`;
};
// { apple: "apple 🍎", banana: "banana 🍎" }
3. Bật noUncheckedIndexedAccess
trong tsconfig.json
Một flag ít được biết đến nhưng rất mạnh mẽ, giúp bạn buộc phải xử lý các khóa hoặc chỉ mục có thể bị thiếu:
{ "compilerOptions": { "noUncheckedIndexedAccess": true }
}
Nó giúp việc truy cập đối tượng và mảng an toàn hơn.
4. Hàm generic có thể tái sử dụng
Hàm generic giữ độ chính xác của kiểu dù giá trị truyền vào là gì:
function identity<T>(value: T): T { return value;
}
5. Sử dụng Record
để ánh xạ khóa và giá trị
Tuyệt vời để định nghĩa cấu hình hoặc phân quyền theo vai trò:
type UserRoles = "admin" | "user";
type Permissions = Record<UserRoles, string[]>;
// { admin: string[]; user: string[] }
6. Các kiểu tiện ích hữu ích: Partial
, Required
, Readonly
, Pick
Các bộ biến đổi kiểu mạnh mẽ:
Partial<T>
→ Biến tất cả thuộc tính thành tùy chọnRequired<T>
→ Biến tất cả thuộc tính thành bắt buộcReadonly<T>
→ Ngăn không cho sửa đổiPick<T, K>
→ Chọn một phần thuộc tính
7. Kiểm tra kiểu tùy chỉnh (Custom Type Guards)
Cho phép bạn thu hẹp kiểu tại runtime và mở khóa khả năng tự động hoàn thành (autocomplete):
function isString(value: unknown): value is string { return typeof value === "string";
}
8. Cấu hình tsconfig.json
phù hợp với môi trường
Dành cho Node.js:
{ "compilerOptions": { "module": "NodeNext", "target": "ES2022" }
}
Dành cho ứng dụng web:
{ "compilerOptions": { "lib": ["ES2022", "DOM"] }
}
9. Kết hợp keyof
+ typeof
để tạo kiểu động
Tạo kiểu từ đối tượng thực tế:
const colors = { primary: "#000", secondary: "#FFF"
}; type ColorKeys = keyof typeof colors;
// "primary" | "secondary"
10. Conditional type phân phối
TypeScript sẽ phân phối conditional type trên từng phần tử của union:
type Fruit = "apple" | "banana" | "orange";
type IsCitrus<T> = T extends "orange" ? true : false; type Result = IsCitrus<Fruit>;
// true | false | false
11. Mở rộng phạm vi toàn cục với declare global
Thêm thuộc tính tùy chỉnh vào window hoặc các đối tượng toàn cục khác:
declare global { interface Window { myCustomProperty: string; }
}
12. Hàm generic với đảm bảo an toàn cho key
Sử dụng keyof
và typeof
để truy cập thuộc tính một cách an toàn:
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key];
}
Lời kết
Những thủ thuật này không chỉ giúp bạn tiết kiệm thời gian—chúng còn giúp mã nguồn của bạn rõ ràng hơn và ít lỗi hơn. TypeScript là một công cụ thiết kế: bạn càng thành thạo nó, bạn càng giao tiếp hiệu quả hơn với đồng đội... và với trình biên dịch.
Bạn đã sử dụng thủ thuật nào trong số này chưa? Có thủ thuật nào mới mẻ với bạn không?