递归可变参数函数可以在打字稿中键入吗?



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

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 不是 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>的函数,并且它还具有类型为UrunVariadic属性。


这是一个简短的测试:

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

在这里,我传递variadicid函数,该函数被注释为获取并返回字符串数组。 然后,生成的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),看起来也不错。


好的,希望这能给你一些方向。 祝你好运!

操场链接到代码

最新更新