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

问题描述 投票:1回答:1

我具有以下通用函数,可以从中派生各种有用的可变参数:

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新手,所以我什至都不知道从哪里开始。

javascript typescript types functional-programming variadic
1个回答
3
投票

这里最大的警告是,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数组并返回U的函数,然后将其转换为Variadic<T, U>Variadic<T, U>是接受T参数并返回Variadic<T, U>的函数,并且它还具有runVariadic类型的U属性。


这里是简短测试:

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

这里我正在传递variadic函数id,该函数被注释为获取并返回字符串数组。然后,生成的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

© www.soinside.com 2019 - 2024. All rights reserved.