我有以下泛型函数,我可以从中导出各种有用的可变参数函数:
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>
的函数,并且它还具有类型为U
的runVariadic
属性。
这是一个简短的测试:
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
arrFold
、comp
和id
的类型相当简单,但是编译器推断出的结果varComp
类型充满了unknown
。 相反,我断言它是Variadic<(x: number) => number, (x: number) => number>
的,因为我知道我们会inc
传递给它。 所以main.runVariadic
被推断为(x: number) => number)
,看起来也不错。
好的,希望这能给你一些方向。 祝你好运!
操场链接到代码