Excessive stack depth comparing types with TS 3.2

5 min read

TS 类型系统支持尾递归优化了,拿下面这个例子就好理解:

type TrimLeft<T extends string> =
    T extends ` ${infer Rest}` ? TrimLeft<Rest> : T;

// error: Type instantiation is excessively deep and possibly infinite.
type Test = TrimLeft<"                                                oops">;

在没有做尾递归优化前,TS 会因为堆栈过深而报错,但现在可以正确返回执行结果了,因为尾递归优化后,不会形成逐渐加深的调用,而是执行完后立即退出当前函数,堆栈数量始终保持不变。

JS 目前还没有做到自动尾递归优化,但可以通过自定义函数 TCO 模拟实现,下面放出这个函数的实现:

function tco(f) {
  var value;
  var active = false;
  var accumulated = [];
  return function accumulator(...rest) {
    accumulated.push(rest);
    if (!active) {
      active = true;
      while (accumulated.length) {
        value = f.apply(this, accumulated.shift());
      }
      active = false;
      return value;
    }
  };
}

核心是把递归变成 while 循环,这样就不会产生堆栈。