Các phương pháp hay nhất trong lập trình TypeScript
Bài viết này giải thích các phương pháp hay nhất trong lập trình TypeScript.
Hướng dẫn này giải thích các phương pháp thực tế để sử dụng kiểu dữ liệu của TypeScript nhằm giảm lỗi và viết mã dễ đọc hơn.
YouTube Video
Các phương pháp hay nhất trong lập trình TypeScript
Lợi ích lớn nhất của TypeScript là 'ngăn ngừa lỗi nhờ kiểu dữ liệu và làm rõ ý định của mã nguồn'.
Các phương pháp hay nhất không chỉ là các quy tắc mà còn là tập hợp các nguyên tắc để viết mã an toàn, dễ đọc và dễ bảo trì. Dưới đây, chúng tôi giới thiệu các phương pháp hay nhất thường được sử dụng trong TypeScript kèm các ví dụ thực tế.
Tránh sử dụng any và luôn đặt định nghĩa rõ ràng cho kiểu dữ liệu
Trước tiên, hãy xem xét vấn đề 'tránh sử dụng any và cung cấp các kiểu dữ liệu có ý nghĩa.'.
any hoàn toàn tắt kiểm tra kiểu dữ liệu, điều này trái với mục đích sử dụng TypeScript. Thay vì dùng any chỉ để mọi thứ hoạt động, điều quan trọng là đưa ra các kiểu dữ liệu càng mô tả càng tốt.
1// Bad
2function parse(data: any) {
3 return data.value;
4}Đoạn mã này có thể chấp nhận bất kỳ giá trị nào, do đó không thể ngăn lỗi khi chạy.
1// Good
2type ParsedData = {
3 value: string;
4};
5
6function parse(data: ParsedData): string {
7 return data.value;
8}Bằng cách định nghĩa kiểu, bạn làm rõ ý định của đầu vào và đầu ra, đồng thời tăng cường độ an toàn.
Luôn định nghĩa rõ cấu trúc đối tượng bằng cách sử dụng type hoặc interface.
Tiếp theo, hãy xem xét vấn đề 'luôn luôn xác định rõ ràng cấu trúc đối tượng bằng cách sử dụng type hoặc interface.'.
Nếu bạn sử dụng đối tượng không theo cấu trúc cụ thể, cấu trúc của chúng có thể trở nên mơ hồ. Luôn trích xuất kiểu dữ liệu để tái sử dụng và dễ bảo trì.
1// Bad
2function createUser(user: { name: string; age: number }) {
3 console.log(user.name);
4}Ngay cả trong những đoạn mã nhỏ, việc phát triển thói quen tách biệt các kiểu dữ liệu là rất quan trọng.
1// Good
2type User = {
3 name: string;
4 age: number;
5};
6
7function createUser(user: User): void {
8 console.log(user.name);
9}Việc đặt tên cho kiểu dữ liệu giúp việc hiểu tổng thể mã nguồn trở nên dễ dàng hơn.
Sử dụng kiểu hợp (union types) để đại diện chính xác cho tất cả các trạng thái có thể
Nếu bạn sử dụng kiểu string hoặc number thô cho điều kiện, giá trị không mong muốn có thể lọt qua. Bằng cách sử dụng kiểu liên hợp, bạn có thể chỉ biểu diễn các trạng thái được phép ở cấp độ kiểu dữ liệu.
1// Bad
2function setStatus(status: string) {
3 console.log(status);
4}Trong đoạn mã này, các chuỗi không có ý nghĩa hoặc giá trị sai sẽ không được phát hiện khi biên dịch.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4function setStatus(status: Status): void {
5 console.log(status);
6}Bằng cách sử dụng kiểu liên hợp, bạn có thể loại bỏ đáng tin cậy các 'trạng thái không thể xảy ra' tại thời điểm biên dịch. Do đó, độ an toàn của các nhánh điều kiện và độ tin cậy của mã nguồn sẽ được cải thiện.
Xử lý null và undefined một cách rõ ràng.
Tiếp theo, hãy xem xét vấn đề 'xử lý rõ ràng null và undefined.'.
Trong TypeScript, việc thể hiện khả năng vắng mặt của một giá trị trong kiểu dữ liệu là rất quan trọng. Nếu bạn để mọi thứ mơ hồ, chúng có thể dẫn đến lỗi khi chạy.
1type User = {
2 name: string;
3 email?: string;
4};email có thể không tồn tại, vì vậy bạn nên xử lý nó với giả định đó.
1function printEmail(user: User): void {
2 if (user.email) {
3 console.log(user.email);
4 }
5}Luôn kiểm tra giá trị tùy chọn trước khi sử dụng.
Chỉ nên sử dụng ép kiểu (as) khi không còn cách nào khác.
Tiếp theo, hãy xem xét vấn đề 'không lạm dụng xác thực kiểu dữ liệu (type assertion).'.
Khẳng định kiểu cho phép tạm thời vượt qua kiểm tra kiểu của TypeScript để khai báo 'Tôi biết giá trị này thuộc kiểu này.'. Lạm dụng chúng sẽ làm giảm an toàn kiểu dữ liệu.
1// Bad
2const value = input as string;Trong đoạn mã này, ngay cả khi giá trị thực tế không phải là một chuỗi, sẽ không xảy ra lỗi nào, điều này có thể gây lỗi khi chạy chương trình. Như được thể hiện trong đoạn mã dưới đây, hãy ưu tiên kiểm tra an toàn bằng cách sử dụng type guards trước tiên.
1// Good
2function isString(value: unknown): value is string {
3 return typeof value === "string";
4}
5
6if (isString(input)) {
7 console.log(input.toUpperCase());
8}Type guard là một cơ chế để xác định kiểu dữ liệu một cách an toàn bằng cách kiểm tra giá trị thực tế. Bằng cách ưu tiên sử dụng type guard thay vì khẳng định kiểu, bạn có thể ngăn ngừa lỗi khi chạy dễ dàng hơn.
Không nên quá phụ thuộc vào suy luận kiểu cho kiểu trả về.
Tiếp theo, hãy xem xét vấn đề 'không quá phụ thuộc vào suy diễn kiểu dữ liệu trả về.'.
Suy luận kiểu của TypeScript rất mạnh mẽ, nhưng an toàn hơn khi khai báo rõ kiểu trả về của các hàm public. Việc này giúp giảm thiểu tác động có thể xảy ra của các thay đổi trong tương lai.
1// Bad
2function sum(a: number, b: number) {
3 return a + b;
4}Hãy thể hiện rõ ý định của bạn ngay cả trong các hàm nhỏ.
1// Good
2function sum(a: number, b: number): number {
3 return a + b;
4}Việc ghi rõ kiểu trả về giúp tăng độ ổn định của API.
Xử lý đầu vào một cách an toàn bằng cách sử dụng unknown.
Tiếp theo, hãy xem xét vấn đề 'chấp nhận đầu vào bên ngoài một cách an toàn bằng cách sử dụng unknown.'.
Đối với đầu vào từ bên ngoài như API, JSON hoặc người dùng nhập vào, hãy sử dụng unknown thay vì any. Bằng cách làm như vậy, bạn đảm bảo rằng tất cả các giá trị đều được xác thực, duy trì an toàn kiểu dữ liệu.
1// Bad
2function handleResponse(data: any) {
3 console.log(data.id);
4}Dưới đây là cách kiểm tra kiểu dữ liệu với unknown.
1// Good
2function handleResponse(data: unknown): void {
3 if (
4 typeof data === "object" &&
5 data !== null &&
6 "id" in data
7 ) {
8 console.log((data as { id: number }).id);
9 }
10}unknown không thể sử dụng trực tiếp; đây là một kiểu cần phải được xác thực. Điều này đặc biệt hiệu quả khi xử lý đầu vào từ bên ngoài.
Tăng sự biểu đạt bằng cách kết hợp các kiểu nhỏ.
Tiếp theo, hãy xem xét vấn đề 'tăng tính biểu đạt bằng cách kết hợp các kiểu dữ liệu nhỏ.'.
Việc định nghĩa một kiểu lớn ngay từ đầu sẽ làm giảm tính dễ đọc và bảo trì. Chia nhỏ các kiểu dữ liệu thành các đơn vị có ý nghĩa và kết hợp chúng khi cần thiết.
1type Id = number;
2
3type UserProfile = {
4 id: Id;
5 name: string;
6};
7
8type UserWithStatus = UserProfile & {
9 status: "active" | "inactive";
10};Xem kiểu dữ liệu như các thành phần giúp sắp xếp tổ chức thiết kế.
type và interface
Ưu điểm của interface
type và interface đều có thể định nghĩa kiểu dữ liệu, nhưng mục đích sử dụng và đặc điểm của chúng khác nhau. Bằng cách sử dụng đúng vai trò, ý định khi định nghĩa kiểu dữ liệu của bạn sẽ rõ ràng hơn.
1// Bad
2type User = {
3 id: number;
4 name: string;
5};
6
7type AdminUser = {
8 id: number;
9 name: string;
10 role: "admin";
11};Nếu bạn lặp lại các phần chung như thế này, mã của bạn sẽ dễ bị ảnh hưởng bởi thay đổi.
1// Good
2interface User {
3 id: number;
4 name: string;
5}
6
7interface AdminUser extends User {
8 role: "admin";
9}interface lý tưởng cho thiết kế liên quan đến mở rộng (extends) và phù hợp nhất để thể hiện 'hình dạng' của đối tượng.
Ưu điểm của type
Ngược lại, type diễn đạt tốt hơn và phù hợp với việc xử lý kiểu hợp và giao.
1// Good
2type Status = "idle" | "loading" | "success" | "error";
3
4type ApiResponse<T> =
5 | { status: "success"; data: T }
6 | { status: "error"; message: string };type rất phù hợp để thể hiện các trạng thái, lựa chọn và kết hợp.
Hướng dẫn lựa chọn giữa type và interface
Theo nguyên tắc chung, sử dụng interface cho cấu trúc đối tượng và hợp đồng, còn type khi bạn cần diễn đạt kiểu hợp, giao hoặc thao tác kiểu dữ liệu.
Cả hai đều hoạt động tương tự, nhưng điều quan trọng là lựa chọn dựa trên mục tiêu giao tiếp lý do tồn tại của kiểu dữ liệu.
Xem kiểu dữ liệu của bạn như tài liệu.
Cuối cùng, hãy xem xét vấn đề 'viết kiểu dữ liệu như tài liệu.'.
Định nghĩa kiểu tốt truyền đạt nhiều thông tin hơn bất kỳ chú thích nào. Điều quan trọng là hướng tới trạng thái mà 'chỉ cần nhìn vào các kiểu dữ liệu là có thể hiểu được đặc tả.'.
1type ApiError = {
2 code: number;
3 message: string;
4 retryable: boolean;
5};Bằng cách này, một trong những điểm mạnh lớn của TypeScript là các định nghĩa kiểu dữ liệu có thể hoạt động như một dạng tài liệu đặc tả.
Tóm tắt
Các phương pháp hay nhất của TypeScript không phải là quá nghiêm ngặt. Bản chất là làm rõ ý định thông qua kiểu dữ liệu và viết mã dễ thích ứng với thay đổi.
Bằng cách tích lũy các quy tắc nhỏ trong quá trình phát triển hàng ngày, bạn sẽ đạt được các kết quả lâu dài như 'dễ review hơn', 'ít lỗi hơn', và 'hiểu rõ hơn cho chính bạn trong tương lai cũng như người khác.'.
Trước hết, bằng cách tự hỏi 'làm thế nào để diễn đạt điều này bằng kiểu dữ liệu?', bạn sẽ viết mã chất lượng cao theo phong cách TypeScript.
Bạn có thể làm theo bài viết trên bằng cách sử dụng Visual Studio Code trên kênh YouTube của chúng tôi. Vui lòng ghé thăm kênh YouTube.