"TS2322: Type 'Timeout' is not assignable to type 'number'" when running unit tests
TypescriptUnit TestingKarma RunnerKarma MochaTypescript Problem Overview
I have two TypeScript packages, and one package (Package A) depends on the other (Package B). Each package has a unit test set up using Karma. When I run unit tests for each individually after installing all dependencies from NPM, the unit tests run fine. However, if I use npm link package-b
in Package A and run Package A's unit tests then, I get the error stated in the title: "TS2322: Type 'Timeout' is not assignable to type 'number'."
The line in question is a call to setTimeout
. After digging, I found that while running the tests separately without npm link
, TypeScript correctly identifies the setTimeout
signature in typescript/lib/lib.dom
as the desired type, but in the failing case after using npm link
it is using using Node's setTimeout
signature in @types/node/index
. I confirmed this by changing the return type on setTimeout
to string
and observing the same error with string
in the place of Timeout
.
What I am not certain of is why the TypeScript compiler has decided to use the alternative definition in this specific case, nor how I can convince it to use the desired definition. I am happy to post some code, but I am not sure what would be useful in this case given all that is on the failing line is the setTimeout
call.
Typescript Solutions
Solution 1 - Typescript
You could try with using window.setTimeout
instead of just setTimeout, this way the typescript one will be explicitly used
Solution 2 - Typescript
You can use:
let timeoutId: null | ReturnType<typeof setTimeout> = null
...
timeoutId = setTimeout(...)
It'll pick correct declaration depending on your context.
I'm seeing this discrepency when using vscode/tsc (NodeJS.Timeout) and running ts-jest (number). This is the only way the whole thing typechecks on both sides.
Solution 3 - Typescript
By default, typescript includes all ./node_modules/@types/*
. If you have ./node_modules/@types/node
there, its timeout typings will override the web typing (that returns a number, and not a NodeJS.Timeout
).
You can remedy to that by explicitly emptying your types in your tsconfig.json
:
{
"compilerOptions": {
"types": []
}
}
Realistically you're probably in a project where you need other types and libs so you might wanna bring back ES and DOM libs:
{
"compilerOptions": {
"types": [],
"lib": ["ESNext", "DOM"]
}
}
Solution 4 - Typescript
You can use something like:
let myTimeOut: NodeJS.Timeout | null = null;
myTimeOut = setTimeout(...);
Then when you reset your variable to initial status, you can simply:
myTimeOut = null;