Reputation: 4213
Let's say I have the following function:
function repeat(times: number, cb: (index: number) => any) {
for (let i = 0; i < times; i++) {
cb(i);
}
}
repeat(3, (i) => console.log(i)); // logs 0 1 2
I want to be able to assert at compile-time that the given number for the number of times to call the callback is an integer. I know that you can do this by taking in a bigint
, however, I would rather not allow for numbers to be larger than the regular number
limit because the function would most likely not even finish. Is there any way to make TypeScript error when the number provided is not an integer?
Upvotes: 5
Views: 2775
Reputation: 4213
You can do this via some awesome string intrinsic behavior with the number
and bigint
type. Basically, the ${bigint}
template literal type matches any sign (+ / -) optional followed by one or more digits. Now, when you take any number
type and interpolate it into a template literal like the following: ${5}
it gets normalized into a regular string type like this "5"
. Therefore, we can take the stringified form of the number and check if it extends the ${bigint}
type. If so, then make the parameter type itself. Otherwise, make it never
and thus force the user to change their type.
type AssertInteger<N extends number> =
number extends N ? N : `${N}` extends `${bigint}` ? N : never;
function repeat<N extends number>(times: AssertInteger<N>, cb: (index: number) => any) {
for (let i = 0; i < times; i++) {
cb(i);
}
}
// dummy function
const dummy = (_: number) => {};
// No Errors 🎉
repeat(1, dummy);
repeat(543.0, dummy);
repeat(-43, dummy);
repeat(0, dummy);
repeat(5e5, dummy);
repeat(0.01e3, dummy);
// Correctly errors 🎉
repeat(1.2, dummy);
repeat(-0.9, dummy);
repeat(543.5, dummy);
repeat(-43.8, dummy);
repeat(-43e-1, dummy);
The number extends N ? N
clause of the AssertInteger
type is just a little check to see if the number
type is an exact number type. If it's inexact (number
), then we will simply let the integer go through because there would be no way to assert it besides some ugly casts.
Upvotes: 4