我正在尝试实现 sumPluck 函数。它将允许调用者指定属于数组中对象的数字类型属性,然后将它们相加。
示例:
type A = { prop: number }
const arr: A[] = [{prop: 1}, {prop: 2}];
pluckSum("prop", arr); // 3
我知道如何输入 pluck,但我似乎无法让我的类型认识到它实际上只处理数字属性。这就是我所拥有的:
type PropertiesOfTypeNames<T, U> = { [K in keyof T]: T[K] extends U ? K : never }[keyof T];
type PropertiesOfType<T, U> = Pick<T, PropertiesOfTypeNames<T, U>>;
type NumberProperties<T> = PropertiesOfType<T, number>;
const pluckSum = <T, K extends keyof NumberProperties<T>>(arr: T[], k: K) =>
pipe(
arr,
R.map(v => v[k]),
R.sum
);
我在 map 下收到一条错误消息:类型“T[string]”无法分配给类型“number”
因此,映射类型似乎并未表明 v[k] 是一个数字属性。我一定是做错了什么。
最佳答案
无论 Typescript 如何,我都想向您展示一种循环融合的替代方法,即消除冗余数组遍历。我使用 (b -> c) -> (a -> c -> d) -> a -> b -> d
类型的有点非正统的组合器获得循环融合(以 Hindley-Milner 表示法),我称之为 contramap2nd
,因为它与二元函数的第二个参数相反射(reflect)射。
长故事,短故事:
// (b -> c) -> (a -> c -> d) -> a -> b -> d
const contramap2nd = g => f => x => y =>
f(x) (g(y));
const arrFold = f => init => xs =>
xs.reduce((acc, x) => f(acc) (x), init);
const add = x => y => x + y;
const prop = k => o => o[k];
const pluckSum = k =>
arrFold(
contramap2nd(
prop(k))
(add)) (0);
console.log(
pluckSum("foo") ([{foo: 1}, {foo: 2}, {foo: 3}]));
您可以对 add
的第二个参数进行反向映射当然,如果您不喜欢的话,可以手动contramap2nd
.
请注意,您可以获得 map
的循环融合, contramap
等人。
关于Typescript 和 Pluck 类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57012247/