Reputation: 196
I have recently started learning Typescript and ran into this problem that I do not understand conceptually. Is it wrong to define parameter type for this function?
setTimeout((a: string) => {
console.log(a); // ==> Prints 5 as a number
}, 1, 5);
Why isn't there type checking performed when calling callback?
How would I go about making typescript throw an error?
What are my solutions in general, when encountering something like this?
Upvotes: 2
Views: 533
Reputation: 329258
This is a known bug in TypeScript, see microsoft/TypeScript#32186. The typings for setTimeout()
look like
setTimeout(handler: TimerHandler, timeout?: number, ...arguments: any[]): number;
which accepts any argument list whatsoever, without caring about the types. And TimerHandler
is defined as just
type TimerHandler = string | Function
which uses the unsafe and untyped Function
type. So setTimeout()
is unfortunately very loosely typed in TypeScript. Often in situations like this you could use declaration merging to add better call signatures, but in this case nothing will stop the compiler from falling back to the unsafe call signature.
This is just a limitation in TS, it seems. The issue is listed as being in the "Backlog", so it's not slated to be addressed anytime soon. You could go to microsoft/TypeScript#32186 and give it a 👍 and show support for it being fixed, but it's not likely to change much.
For now there are only workarounds.
You could decide to use your own local copy of the lib.dom.ts
library, where you modify the definition of safeTimeout()
, but that would be very annoying to maintain.
A workaround you could use is to wrap setTimeout
in your own function that is more strongly typed, and then use only that Perhaps something like this:
const safeSetTimeout: <A extends any[]>(
handler: (...args: A) => void, timeout?: number, ...args: A
) => number = setTimeout;
That uses generics to strongly type the args
expected by the handler
callback to be same as that passed as the args
passed to setTimeout()
itself. Let's see it work:
safeSetTimeout((a: string) => {
console.log(a);
}, 1, 5); // <-- error! number is not assignable to string
// ~
Great, we get the expected compiler error on 5
, saying that it should be a string
and not a number
. That's about as good as it gets for now, unfortunately.
Upvotes: 3