Phân tích tĩnh

Phân tích tĩnh là một loại kiểm thử cung cấp khả năng kiểm tra tự động đối với mã của bạn mà không thực sự chạy mã hoặc phải viết kiểm thử tự động. Có thể bạn đã thấy loại hình kiểm thử này nếu sử dụng IDE như VSCode – hoạt động kiểm tra loại do TypeScript thực hiện là một loại phân tích tĩnh và có thể xuất hiện dưới dạng các dòng nguỵ tạo bên dưới lỗi hoặc cảnh báo.

ESLint

ESLint là một công cụ có thể cung cấp ý kiến phản hồi về các vấn đề có thể xảy ra trong cơ sở mã của bạn. Những sự cố này có thể thuộc loại an toàn nhưng lại có lỗi hoặc hành vi không chuẩn mực theo cách riêng của chúng. ESLint cho phép bạn áp dụng một số quy tắc được kiểm tra trên cơ sở mã của bạn, bao gồm nhiều quy tắc trong bộ "đề xuất".

Một ví dụ điển hình về quy tắc ESLint là quy tắc không an toàn cuối cùng. Điều này ngăn bạn viết câu lệnh làm sửa đổi luồng điều khiển của chương trình bên trong một khối finally. Đây là một quy tắc tuyệt vời, vì làm như vậy là một cách viết JavaScript khác thường, có thể khó tuân theo. Tuy nhiên, đó cũng là điều mà quy trình xem xét mã lành mạnh có thể phát hiện được.

  try {
    const result = await complexFetchFromNetwork();
    if (!result.ok) {
      throw new Error("failed to fetch");
    }
  } finally {
    // warning - this will 'overrule' the previous exception!
    return false;
  }

Do đó, ESLint không phải là giải pháp thay thế cho một quy trình xem xét an toàn (và hướng dẫn về kiểu giúp xác định hình thức của cơ sở mã), vì nó không ghi lại mọi phương pháp không chính thống mà nhà phát triển có thể cố gắng đưa vào cơ sở mã của bạn. Hướng dẫn về Các phương pháp sử dụng tiếng Anh của Google có một phần ngắn trình bày về "cách đơn giản hoá".

ESLint cho phép bạn phá vỡ quy tắc và chú thích mã là "được phép". Ví dụ: bạn có thể cho phép logic trước đó bằng cách chú thích logic đó như sau:

  finally {
    // eslint-disable-next-line no-unsafe-finally
    return false;
  }

Nếu bạn thấy mình liên tục vi phạm một quy tắc, hãy cân nhắc tắt quy tắc đó. Các công cụ này khuyến khích bạn viết mã theo một cách nào đó, nhưng có thể nhóm của bạn đã viết mã theo cách khác và đã nhận ra rủi ro của phương pháp đó.

Cuối cùng, việc bật các công cụ phân tích tĩnh trên một cơ sở mã lớn có thể tạo ra nhiều tiếng ồn không hữu ích (và bận rộn cần tái cấu trúc) so với mã hoạt động hiệu quả. Vì vậy, việc bật chế độ này từ sớm trong vòng đời của dự án sẽ dễ dàng hơn.

Các trình bổ trợ ESLint để hỗ trợ trình duyệt

Bạn có thể thêm một trình bổ trợ vào ESLint để gắn cờ việc sử dụng API không được hỗ trợ rộng rãi hoặc không được danh sách trình duyệt mục tiêu hỗ trợ. Gói eslint-plugin-compat có thể cảnh báo cho bạn khi người dùng có thể không sử dụng được API, nhờ đó, bạn không phải liên tục theo dõi.

Kiểm tra loại để phân tích tĩnh

Khi học JavaScript, các nhà phát triển mới thường được làm quen với ý tưởng rằng đó là một ngôn ngữ được nhập yếu. Điều này nghĩa là bạn có thể khai báo một biến là một kiểu, sau đó sử dụng cùng một vị trí cho một kiểu hoàn toàn khác. Ngôn ngữ này tương tự như Python và các ngôn ngữ tập lệnh khác, nhưng không giống các ngôn ngữ đã biên dịch như C/C++ và Rust.

Loại ngôn ngữ này có thể phù hợp để bắt đầu – và chính sự đơn giản này đã khiến JavaScript trở nên phổ biến – nhưng thường sẽ gây ra lỗi cho một số cơ sở mã hoặc ít nhất là gây ra lỗi khó hiểu. Ví dụ: bằng cách truyền number trong đó string hoặc một loại đối tượng được mong đợi, giá trị được nhập không chính xác đó có thể truyền qua nhiều thư viện trước khi gây ra TypeError khó hiểu.

TypeScript

TypeScript là giải pháp chủ đạo nhất cho tình trạng thiếu thông tin nhập của JavaScript. Khoá học này áp dụng rộng rãi tính năng này. Mặc dù đây không phải là khoá học về TypeScript, nhưng đây có thể là một phần quan trọng trong hộp công cụ của bạn vì nó cung cấp tính năng phân tích tĩnh.

Ví dụ nhanh: mã này dự kiến sẽ được cung cấp một lệnh gọi lại chấp nhận tên string và độ tuổi number:

const callback = (name: string, age: string): void => {
  console.info(name, 'is now', age, 'years old!');
};
onBirthday(callback);

Tạo lỗi sau đây khi chạy thông qua TypeScript hoặc ngay cả khi di chuột qua trong một IDE:

bad.ts:4:12 - error TS2345: Argument of type '(name: string, age: string) => void' is not assignable to parameter of type '(name: string, age: number) => void'.
  Types of parameters 'age' and 'age' are incompatible.
    Type 'number' is not assignable to type 'string'.

4 onBirthday(callback);
             ~~~~~~~~

Found 1 error in bad.ts:4
Mã trong ví dụ trước, được hiển thị trong IDE với thông báo lỗi xuất hiện trong cửa sổ bật lên.
VSCode cho biết rằng bạn đã chuyển sai loại.

Cuối cùng, mục tiêu của việc sử dụng TypeScript là ngăn chặn những lỗi như thế này (độ tuổi phải là number chứ không phải string) xuất hiện trong dự án của bạn. Loại lỗi này có thể khó phát hiện khi sử dụng các loại kiểm thử khác. Ngoài ra, hệ thống kiểu chữ có thể đưa ra phản hồi trước khi viết bài kiểm thử. Điều này có thể giúp quá trình viết mã trở nên dễ dàng hơn bằng cách sớm cung cấp cho bạn thông tin phản hồi về lỗi loại khi bạn đang phát triển phần mềm, thay vì khi mã cuối cùng chạy được.

Phần khó khăn nhất khi sử dụng TypeScript là thiết lập đúng cách. Mỗi dự án cần có một tệp tsconfig.json. Tệp này chủ yếu được chính công cụ dòng lệnh tsc sử dụng nhưng cũng được các IDE như VSCode đọc cùng với nhiều công cụ xây dựng và công cụ khác, bao gồm cả Vitest. Tệp này chứa hàng trăm tuỳ chọn và cờ. Bạn có thể tìm thấy một số tài nguyên phù hợp để thiết lập tệp này tại đây:

Mẹo chung về TypeScript

Khi thiết lập và sử dụng TypeScript thông qua tệp tsconfig.json, hãy lưu ý những điều sau:

  • Hãy đảm bảo các tệp nguồn của bạn thực sự được đưa vào và kiểm tra. Nếu một tệp "không có lỗi" bí ẩn thì đó có thể là do tệp đó chưa được kiểm tra.
  • Việc mô tả rõ ràng các loại và giao diện bên trong tệp .d.ts thay vì mô tả ngầm định khi bạn viết hàm có thể giúp cơ sở mã của bạn dễ kiểm thử hơn. Sẽ dễ dàng hơn khi bạn viết bản mô phỏng và phiên bản mã "giả" khi các giao diện liên quan đều rõ ràng. .

TypeScript ngầm ẩn bất kỳ

Một trong những lựa chọn cấu hình mạnh mẽ và bổ ích nhất của TypeScript là cờ noImplicitAny. Tuy nhiên, việc bật chế độ này cũng thường khó nhất, đặc biệt nếu bạn đã có cơ sở mã lớn. (Cờ noImplicitAny sẽ được bật theo mặc định nếu bạn đang ở chế độ strict, nhưng không được bật ở chế độ khác.)

Cờ này sẽ khiến hàm này trả về lỗi:

export function fibonacci(n) {
  if (n <= 1) {
    return 0;
  } else if (n === 2) {
    return 1;
  }
  return fibonacci(n - 1) + fibonacci(n - 2);
}

Mặc dù với tư cách là một người đọc, n phải là một số khá rõ ràng, nhưng TypeScript không thể xác nhận điều này một cách chắc chắn. Nếu bạn đang sử dụng VSCode, việc di chuột qua hàm sẽ mô tả hàm đó như sau:

function fibonacci(n: any): any

Các phương thức gọi của hàm này sẽ có thể truyền giá trị thuộc loại any (một loại cho phép bất kỳ loại nào khác), không chỉ là number. Bằng cách bật cờ noImplicitAny, bạn có thể bảo vệ loại mã này trong quá trình phát triển mà không cần viết các kiểm thử logic nghiệp vụ mở rộng cho mã chuyển sai loại dữ liệu ở những vị trí cụ thể.

Cách khắc phục đơn giản ở đây là đánh dấu cả đối số n và loại dữ liệu trả về của fibonaccinumber.

Cờ noImplicitAny không ngăn bạn một cách rõ ràng viết any trong cơ sở mã của mình. Bạn vẫn có thể viết một hàm chấp nhận hoặc trả về loại any. Nó chỉ đảm bảo rằng bạn cung cấp cho mỗi biến một kiểu.