可以在 Typescript 中键入递归可变参数函数吗?

Can recursively variadic functions be typed in Typescript?

我有以下通用函数,我可以从中导出各种有用的可变参数函数:

const variadic = f => {
  const go = args =>
    Object.defineProperty(
      arg => go(args.concat([arg])),
      "runVariadic",
      {get: function() {return f(args)}, enumerable: true});

  return go([]);
};

const arrFold = alg => zero => xs =>
  xs.reduce((acc, x) => alg(acc) (x), zero);

const comp = f => g => x => f(g(x));
const id = x => x;

const varComp = variadic(arrFold(comp) (id));

const inc = x => x + 1;

const main = varComp(inc) (inc) (inc) (inc) (inc);

console.log(
  main.runVariadic(0)); // 5

这种递归可变接口允许我在不依赖方法链的情况下维护平面应用程序语法。此外,我可以部分应用和组合此类功能。不幸的是 variadic 和派生的 varComp 有无限类型。我依稀记得在 Haskell 中有一种方法可以键入此类函数,但它需要大量类型机制,namley 高级语言扩展。在 Typescript 中输入它们有技巧吗?

我是一个 Typescript 新手,所以我什至不知道从哪里开始。

这里最大的警告是 TypeScript 的编译器几乎不可能按照您希望的方式推断类型;您可能经常会发现自己需要手动指定类型参数,甚至 assert 特定函数是通用函数。 TypeScript 不是 Haskell,也不想成为(很多)。

话虽这么说,这里是 variadic 的一种可能输入:

interface Variadic<T, U> {
  (x: T): Variadic<T, U>
  runVariadic: U,
}

const variadic = <T, U>(f: (args: T[]) => U) => {
  const go = (args: T[]): Variadic<T, U> =>
    Object.defineProperty(
      (arg: T) => go(args.concat([arg])),
      "runVariadic",
      { get: function () { return f(args) }, enumerable: true });

  return go([]);
}

这个想法是 variadic 接受一个函数,该函数采用 T 和 returning 一个 U 数组,并将其转换为 Variadic<T, U>. Variadic<T, U> 是一个接受 T 参数和 return 一个 Variadic<T, U> 的函数,它还有一个 runVariadic 属性 类型 U.


这是一个简短的测试:

const str = variadic((args: string[]) => args)("hey")("you")("guys").runVariadic; // string[]
console.log(str) // ["hey", "you", "guys"]

我在这里传递 variadic id 函数,它被注释为接受和 return 一个字符串数组。然后得到的Variadic<string, string[]>可以一个接一个地接受任意数量的string参数,最后它的runVariadic属性被编译器推断为一个string[],正如控制台日志所证实的那样。


对于您的测试代码,必须进行大量手动输入和断言:

const arrFold = <T, U>(alg: (x: T) => (y: U) => T) => (zero: T) => (xs: U[]) =>
  xs.reduce((acc, x) => alg(acc)(x), zero);
const comp = <T, U>(f: (x: T) => U) => <V>(g: (x: V) => T) => (x: V) => f(g(x));
const id = <T>(x: T) => x;

const varComp = variadic(arrFold(comp)(id)) as
  Variadic<(x: number) => number, (x: number) => number>;

const inc = (x: number) => x + 1;

const main = varComp(inc)(inc)(inc)(inc)(inc);
console.log(
  main.runVariadic(0)); // 5

arrFoldcompid 的类型相当简单,但编译器推断的 varComp 的结果类型充满了 unknown秒。相反,我断言它是 Variadic<(x: number) => number, (x: number) => number>,因为我知道我们将把 inc 传递给它。所以main.runVariadic推断为(x: number) => number),看起来也不错


好的,希望能给你一些指导。祝你好运!

Playground link to code