The Trouble with TypeScript Timeouts
- Comments:
- 0
I've run into this problem a number of times where I'm developing frontend TypeScript and after I import a package, I suddenly find throughout my project I'm receiving:
error TS2322: Type 'Timeout' is not assignable to type 'number'.
If you are working on a node project rather than a front end project this can also manifest itself as the opposite Type 'number' is not assignable to type 'Timeout'
Usually this happens because a dependency you've included has included @types/node
under dependencies
when it should have included it under devDependencies
. Usually, unless you are actually developing a type package, @types
packages should be a dev dependency.
The ideal solution would be for the package to fix the problem and you to pull the corrected version of the package. This however might not be possible, with the issue going ignored for months if not years - as on Facebook's Jest package [1] [2].
So let us take the following example line
let x : number = setTimeout(() => console.log("foo"), 1000);
Your immediate reaction might be to either change the definition to let x : Timeout
or eliminate the explicit type definition all together ala
let x = setTimeout(() => console.log("foo"), 1000);
Both of these options work.
The first option of setting it to Timeout
suffers from two issues though. Firstly it's fragile - presumably you're not explicitly including @types/node
so the change to the environment here is a side effect you're coding around. Should the specific packages you include change, or said package correct this it will break.
The second option of implicit typing will work in a lot of cases just fine. There are many cases where you'd want for instance to define the variable in a higher scope than you are setting it's value - as a trivial example
class Foo {
private x? : number;
start() {
this.x = setTimeout(() => console.log("foo"), 1000);
}
stop() {
clearTimeout(this.x);
}
}
The answer is frustratingly simple. We can just use the ReturnType
utility type to set our type to the return type of setTimeout. We can do this using ReturnType<typeof setTimeout>
.
Our examples from above become
let x : ReturnType<typeof setTimeout> = setTimeout(() => console.log("foo"), 1000);
and
class Foo {
private x?: ReturnType<typeof setTimeout>;
start() {
this.x = setTimeout(() => console.log("foo"), 1000);
}
stop() {
clearTimeout(this.x);
}
}
Similarly, if you are using setInterval
you can utilize type ReturnType<typeof setTimeout>
for similar purposes.