Tìm kiếm trang web

typescript - "TS2322: Kiểu 'Timeout' không thể gán cho kiểu 'number'" khi chạy các bài kiểm tra đơn vị


Tôi có hai gói TypeScript, và một gói (Gói A) phụ thuộc vào gói kia (Gói B). Mỗi gói đều có bộ kiểm thử đơn vị được thiết lập sử dụng Karma. Khi tôi chạy kiểm thử đơn vị cho mỗi gói riêng lẻ sau khi cài đặt tất cả các phụ thuộc từ NPM, các kiểm thử đều chạy tốt. Tuy nhiên, nếu tôi sử dụng npm link package-b trong Gói A và chạy kiểm thử đơn vị của Gói A, thì tôi nhận được lỗi đã nêu trong tiêu đề: "TS2322: Type 'Timeout' không thể gán cho type 'number'."

Dòng đang được nhắc đến là một lời gọi đến setTimeout. Sau khi tìm hiểu, tôi nhận ra rằng khi chạy các bài kiểm tra riêng lẻ mà không dùng npm link, TypeScript nhận diện chính xác chữ ký của setTimeout trong typescript/lib/lib.dom là kiểu mong muốn, nhưng trong trường hợp thất bại sau khi sử dụng npm link, nó lại sử dụng chữ ký setTimeout của Node trong @types/node/index. Tôi đã xác nhận điều này bằng cách thay đổi kiểu trả về trên setTimeout thành string và quan sát thấy lỗi tương tự với string thay thế cho Timeout.

Điều mà tôi không chắc chắn là tại sao trình biên dịch TypeScript lại quyết định sử dụng định nghĩa thay thế trong trường hợp cụ thể này, cũng như cách tôi có thể thuyết phục nó sử dụng định nghĩa mong muốn. Tôi sẵn sàng đăng một số đoạn mã, nhưng tôi không chắc điều gì sẽ hữu ích trong trường hợp này vì tất cả những gì có trên dòng gặp lỗi chỉ là lời gọi setTimeout.

Câu trả lời #1

Bạn có thể thử sử dụng window.setTimeout thay vì chỉ setTimeout, theo cách này phiên bản của TypeScript sẽ được sử dụng rõ ràng

Câu trả lời #2

Bạn có thể sử dụng:

let timeoutId: null | ReturnType<typeof setTimeout> = null
...
timeoutId = setTimeout(...)

Nó sẽ chọn tuyên bố đúng tùy thuộc vào ngữ cảnh của bạn.

Tôi đang thấy sự khác biệt này khi sử dụng vscode/tsc (NodeJS.Timeout) và chạy ts-jest (number). Đây là cách duy nhất mà toàn bộ mọi thứ kiểm tra kiểu trên cả hai bên.

Câu trả lời #3

Theo mặc định, Typescript bao gồm tất cả ./node_modules/@types/*. Nếu bạn có ./node_modules/@types/node ở đó, các kiểu timeout của nó sẽ ghi đè kiểu web (mà trả về một số, chứ không phải NodeJS.Timeout).

Bạn có thể khắc phục điều đó bằng cách rõ ràng xóa các kiểu trong tsconfig.json của bạn:

{
  "compilerOptions": {
    "types": []
  }
}

Thực tế là bạn có lẽ đang ở trong một dự án nơi bạn cần các loại và thư viện khác, vì vậy bạn có thể muốn mang lại các thư viện ES và DOM:

{
  "compilerOptions": {
    "types": [],
    "lib": ["ESNext", "DOM"]
  }
}

Câu trả lời #4

Thêm vào câu trả lời của Mirek Rusin.

Đối với các lần gọi clearTimeout, bạn nên sử dụng kiểu undefined thay vì null

 let timeoutId: undefined | ReturnType<typeof setTimeout>;

 timeoutId = setTimeout(() => {
   // ...
 }, 1000);

 clearTimeout(timeoutId);

Bài viết liên quan