如何为compose
添加类型?
问题基本上归结为为此编写类型:
const compose = (...funcs) => x => funcs.reduce((acc, func) => func(acc), x);
并使用它:
compose(x => x + 1, x => x * 2)(3);
在此示例中,compose的类型推断为:
const compose: (...funcs: any[]) => (x: any) => any
这只是一堆any
...
是否有一种向compose
添加类型的好方法?
答案 0 :(得分:3)
虽然无法输入这样的函数来接受任意数量的函数,但我们可以编写一个版本compose
,其重载次数高达给定数量的函数。对于大多数实际应用,它应该足以允许最多5个函数的组合,并且我们总是可以在需要时添加更多的重载。
我还添加了一个重载,当类型没有不同时,我们只有一个类型在整个compose中被处理。如果参数类型和返回类型相同,这允许传入任意数量的函数。
function compose<A, B, C, D, E, F>(fn: (p: A) => B, fn2: (p: B) => C, fn3: (p: C) => D, fn4: (p: D) => E, fn5: (p: E) => F): (p: A) => F
function compose<A, B, C, D, E>(fn: (p: A) => B, fn2: (p: B) => C, fn3: (p: C) => D, fn4: (p: D) => E): (p: A) => E
function compose<A, B, C, D>(fn: (p: A) => B, fn2: (p: B) => C, fn3: (p: C) => D): (p: A) => D
function compose<A, B, C>(fn: (p: A) => B, fn2: (p: B) => C): (p: A) => C
function compose<T>(...funcs: Array<(p: T) => T>) : (p: T) => T // Special case of parameter and return having the same type
function compose(...funcs: Array<(p: any) => any>) {
return (x: any) => funcs.reduce((acc, func) => func(acc), x)
};
// Usage
// First argument type must be specified, rest are inferred correctly
let fn = compose((x : number) => x + 1, x => x * 2); // fn will be (p: number) => number
let d = fn(3); // d will be number
let fn2 = compose((x : number) => x + 1, x => x * 2, x=> x.toString(), x => x.toLowerCase()); // fn will be (p: number) => string and we get intelisense and type safety in each function
// Any number of functions but a single type
let fnMany = compose((x : number) => x + 1, x => x * 2, x => x * 2, x => x * 2, x => x * 2, x => x * 2, x => x * 2);
答案 1 :(得分:2)
你可以介绍一些类型信息,我通常建议采用迭代方法,例如这个早期版本不是很灵活 - 但最终类型是正确的:
interface Composer {
(...funcs: { (arg: any): any }[]): (arg: any) => number;
}
const compose: Composer = (...funcs) => x => funcs.reduce((acc, func) => func(acc), x);
// result: number
const result = compose(x => x + 1, x => x * 2)(3);
您可以针对您的特定用途进行迭代。例如,最终类型可能需要是通用类型,因此您可以将Composer
升级为Composer<T>
。或者你可能想让中间类型更不宽容(它们目前可以是任何类型,但你也可以改进它)。
最终,您需要决定在何处修复类型,或引入动态类型,或者完全停止关注Composer
类型内的类型。
答案 2 :(得分:1)
可能不是。
在TypeScript之外思考片刻并考虑用Hindley-Milner推理的ML型语言,两种函数的组合类型将是
compose2: (a -> b) -> (b -> c) -> a -> c
compose2 f g x = g(f x)
如果您有三个功能,则类型为
compose3: (a->b) -> (b->c) -> (c->d) -> a -> d
继续前进
compose4: (a->b) -> (b->c) -> (c->d) -> (d->e) -> a -> e
你希望编写一个compose函数,它接受任意数量的函数(通过rest参数),然后通过reduce进行合成。这非常酷,但你真的无法锁定所有中间函数的所有中间类型,因为一般来说它们都非常非常不同。
处理您希望编写任意数量的函数的灵活性的唯一方法是使(至少)所有中间函数都具有类型
any -> any
即使TypeScript的类型变量与ML系列中的那些语言一样复杂,但是对于任意数量的函数的需求使得无法避免any
s(除非你想将所有函数限制为a->a
,或您特定的TypeScript示例number=>number
)。