不要问为什么我要在Map的子类上实现一个reduce方法:
const nah = Symbol('not-an-arg');
class MapArray<A, B> extends Map<A, B> {
reduce<T = [A, B]>(f: (prev: T, next: [A, B]) => any, initialVal?: T) {
let prev = arguments.length > 1 ? arguments[1] : nah;
for (const [k, v] of this) {
if (prev === nah) {
prev = [k, v];
continue;
}
prev = f(prev, [k, v]);
}
return prev;
}
}
它不是最漂亮的东西,但它基本有效。问题是打字。如果我这样做:
new MapArray<string,boolean>().reduce((a,b) => 'whatever');
和
new MapArray<string,boolean>().reduce((a,b) => 'whatever', []);
我希望它能够捕获第二个参数[]
的类型使用我的无效的版本的TS,我尝试这样做:
type X = (if initialVal ? typeof initialVal : [A, B])
reduce<R = any, T = X>(f: (prev: T, next: [A, B]) => R, initialVal?: T);
这显然是完全错误的,但希望你能明白,怀疑这是可能的。有没有人知道我是否可以捕获initialVal
的类型,如果它传递,如果没有,默认为[A,B]
?
的问题是:TS操场
它可以编译,但是"a不是可迭代的"(a是reduce回调函数的第一个参数)…A是不可迭代的,因为A是55。
一个解决方案可能是使用undefined
代替您的nah
和方法重载:
class MapArray<A, B> extends Map<A, B> {
reduce(f: (prev: [A, B], next: [A, B]) => any): [A, B];
reduce<T>(f: (prev: T, next: [A, B]) => any, initialVal: T): T;
reduce<T>(f: (prev: T | [A, B], next: [A, B]) => any, initialVal?: T) {
let prev: ([A, B] | T | undefined) = initialVal;
let index = 0;
for (const [k, v] of this) {
if (prev === undefined) {
prev = [k, v];
continue;
}
prev = f(prev, [k, v]);
}
return prev;
}
}
new MapArray<string,boolean>().reduce((a,b) => 'whatever');
new MapArray<string,boolean>().reduce((a,b) => 'whatever', []);
游乐场我发现这与@Alexander Mills的解决方案非常相似。
这是为了尽可能接近您的原始代码,但我想指出的是,函数f
的返回类型应该与prev
相同,在这种情况下,示例函数(string
"whatever"
)的返回值不工作。
这并不能100%回答最初的问题,但它可能对某些人有所帮助。
部分解决方案是像这样使用重载:
reduce<R = any>(f: (prev: [A, B], next: [A, B]) => R): R;
reduce<R = any, T = any>(f: (prev: T, next: [A, B]) => R, initialVal?: T) {...}
这不能真正作为OP的答案的原因是,我想获取用户提供的初始val的类型,而不是必须用泛型参数声明它。
再一次,我们要做的是:
reduce<R = any>(f: (prev: [A, B], next: [A, B]) => R): R;
reduce<R = any, T = typeof initialVal>(f: (prev: T, next: [A, B]) => R, initialVal?: <T>) {...}