In TypeScript version 4.5, the feature of tail call optimization was introduced for recursive generics. The code snippet below calculates Fibonacci numbers (in unary) up to F12, but encounters an error when trying to compute F13 due to the "Type instantiation is excessively deep and possibly infinite" exception. This specific implementation of the Fibonacci function intentionally involves two non-tail-call positions for self-calling, serving as a demonstration.
The sole recursive function in this example is Run
; the other functions (and references based on the interface
) should not significantly impact the stack depth. What prevented TCO from working here, and how can it be resolved?
type Done<A> = { type: 'done', value: A };
type More<F, X> = { type: 'more', value: X, fn: F };
type FlatMap1<X, F> = { type: 'flatMap', value: X, fn: F }
// Interfaces and types omitted for brevity...
type R1 = Run<Fib<'1111111111111'>>
// Additional utilities also included in the original snippet...
What happens when TCO works?
An equivalent JavaScript code snippet, purposely made complex, is capable of calculating even larger Fibonacci numbers (exceeding F35). However, this approach requires converting tail recursion into manual loop iteration and implementing binary number usage instead of unary. The primary limitation lies within the heap size, as the entire computation process has been trampolined. To learn more about this technique, refer to the articles linked above.
// JavaScript code for enhanced Fibonacci calculation provided in the original snippet...