I'm trying to find a way to typesafe a recursion (which returns a number by the end) using generics.
What I tried:
function recursion<N,T>(value:N, lim:N):T|N {
if(value < lim) return recursion<N, T>(value+1,lim);
return value;
}
recursion<number, Function>(0,10);
Despite passing type number
, Typescript decided to give me an error:
TSError: ⨯ Unable to compile TypeScript:
src/main.ts:2:41 - error TS2365: Operator '+' cannot be applied to types 'N' and 'number'.
2 if(value < lim) return recursion<N, T>(value+1,lim);
I assumed that operations were possible as long as I passed number
type on the generic, but it doesn't seem to be the case. Why is that and is there any possible workaround?
Tried (doesn't work):
return recursion<number, Function>(value+1,lim)
Log:
src/main.ts:2:53 - error TS2365: Operator '+' cannot be applied to types 'N' and 'number'.
2 if(value < lim) return recursion<number, Function>(value+1,lim);
~~~~~~~
src/main.ts:2:61 - error TS2345: Argument of type 'N' is not assignable to parameter of type 'number'.
2 if(value < lim) return recursion<number, Function>(value+1,lim);
function recursion<N, T>(value:N, lim:N): T | N {
if(value < lim) return recursion<N, T>(value+1,lim);
return value;
}
Why is that?
recursion
takes two generic that may or may not have a +
operator defined on it. Seeing this signature, it seems like you can pass value
as anything (like an object) for the parameter. If Typescript allows that, it wouldn't lead to the expected behaviour.
Also, you know that in your case, you are (ultimately) returning the type of value. So, T
is known to us, and it is of type N
because that's what the termination condition of the recursion is.
How to fix it?
// 1. Make sure value has compatible type
function recursion<N extends number>(value: N, lim: N): N {
if (value < lim) {
// 2. Downcast to N
return recursion((value + 1) as N, lim);
}
return value;
}
We need to make sure the operators work on types. We are applying <
and +
operators on value
and lim
so, we need to make sure value and lim are of compatible types. So, we need to ensure that N, and T supports those operations, hence, extended from number
(you may add more types using union type).
The +
operator returns a number, which may not be compatible with N
. So, we need to explicitly cast it as N to be compatible with recursive call.
We should also explicitly tell the return type, that the function ultimately returns, so return is N
and not a function.
Honestly, for this exact problem, I think using generic is an overkill. I'd rather just stick to the actual types instead. Like this