124

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.

dawsonc623
  • 1,841
  • 2
  • 16
  • 26
  • Does this answer your question? [TypeScript - use correct version of setTimeout (node vs window)](https://stackoverflow.com/questions/45802988/typescript-use-correct-version-of-settimeout-node-vs-window) – Rafael Tavares Oct 06 '21 at 15:58

7 Answers7

243

You could try with using window.setTimeout instead of just setTimeout, this way the typescript one will be explicitly used

Pablo Marcano
  • 2,635
  • 1
  • 11
  • 6
135

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.

Mirek Rusin
  • 18,820
  • 3
  • 43
  • 36
  • 1
    But what if you also need to call `clearTimeout`? (e.g. when the reason to even use `setTimeout` was for debouncing) In my situation for some reason TS expects `setTimeout` to return `NodeJS.Timeout` but it also still expects `clearTimeout` to receive a number... – targumon Jul 23 '21 at 23:08
  • 4
    If you need to use `clearTimeout()` you can just cast the variable to a `Number()`: `clearTimeout(Number(timeoutId))` – Ezra Free Sep 07 '21 at 17:49
  • for me `let timeout: string | number | ReturnType;` worked. I am creating a debounce function. – sohammondal Oct 14 '22 at 13:08
35

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"]
  }
}
Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
  • 12
    Bothers me that this is not the accepted answer. That accepted answer feels incredibly like the StackOverflow stereotype of: "Q: How do I use X? A: You don't, use Y instead, using X is dumb." – Michael Ziluck Feb 12 '22 at 18:50
  • 1
    What to do, if you already have some types included in your project, so you can't reset them, but still doesn't work? The solution I came up with is timeOut = setTimeout(...) as unknown as number; Thereby trying to tell the compiler that setTimeout indeed returns a number here. The 'as unknown' is needed, because otherwise ts complains that there is no overlap between the types. Thoughts? – Benedikt Böhm Jun 21 '22 at 16:31
  • 1
    I'd rather use several tsconfigs per env (one for tests, one for backend, etc), which all extend from a base config, but each with different `.compilerOptions.types` – Nino Filiu Jun 21 '22 at 16:44
  • I tried this but it still picks up node typings. It's in create-react-app project if it matters. – Marcin Wisnicki Nov 02 '22 at 19:05
  • 1
    `"types": []` doesn't work for me, there is a bug https://github.com/microsoft/TypeScript/issues/37708 But it may start working in TS v5 https://github.com/microsoft/TypeScript/pull/51715 – RussCoder Mar 07 '23 at 09:39
0

types when using prefix window.

setTimeout initialization ( you can optionally initialize to null )

let timeout: NodeJS.Timeout | number | null = null;

usage

timeout = window.setTimeout(() => console.log("Timeout"), 1000);

clear

window.clearTimeout(timeout);
Anjan Talatam
  • 2,212
  • 1
  • 12
  • 26
0

Adds to Mirek Rusin answer.

For calls clearTimeout you should to use type undefined instead of null

 let timeoutId: undefined | ReturnType<typeof setTimeout>;

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

 clearTimeout(timeoutId);
Alexander
  • 291
  • 4
  • 13
-3

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;
-3

For me helped "strict": false in tsconfig.json

Griha Mikhailov
  • 655
  • 1
  • 9
  • 16