typescript - How correctly type function that updates values of specified fields of object
问题描述
I've implemented a function that accepts object, callback and array containing keys of this object. This function updates value of every field specified in the array using callback provided in arguments.
Example:
const foo = { a: 3, b: 4, c: 4 }
const bar = updateFields(foo, (val) => val + 10, ['a', 'b'])
console.log(bar) // { a: 13, b: 14, c: 4 }
Right now I have this function implemented and tested, so it works ok.
const updateFields = <T extends {}, K extends keyof T, V>(
obj: T,
callback: (val: T[K]) => V,
fields: K[]
) =>
Object.entries<T[K]>(obj).reduce(
(acc, [key, value]) => {
if (fields.includes(key as K)) {
acc[key] = callback(value)
} else {
acc[key] = value
}
return acc
},
{} as any
)
What I'm interested in is how to correctly write types for this function, especially the return type, as right now I have it implicitly set to any
.
解决方案
updateFields
will have return type { [P in K]: V } & Omit<T, K>
- all properties K
in fields
are updated to value V
while the rest of the properties is preserved.
In reduce
operations there is always some form of assertion/cast necessary, if a new object is iteratively constructed one property after the other. In your concrete example you can ease up the function body by iterating over the fields
array, not the original object properties of T
. That will save you the key as K
cast and conditional logic.
const updateFields = <T extends object, K extends keyof T, V>(
obj: T,
callback: (val: T[K]) => V,
fields: K[]
): Omit<T, K> & { [P in K]: V } => ({
...obj,
...fields.reduce(
(acc, cur) => {
// ... or use spread here
acc[cur] = callback(obj[cur]);
return acc;
},
{} as { [P in K]: V } // this cast is (almost always) necessary
)
});
Let's make the example more obvious by choosing a string type for c
:
const foo = { a: 3, b: 4, c: "foo" };
// const bar: Pick<{a: number; b: number; c: string}, "c"> & {a: number; b: number;}
const bar = updateFields(foo, val => val + 10, ["a", "b"]);
console.log(bar); // { a: 13, b: 14, c: "foo" }
推荐阅读
- flutter - 颤动的火力基地列表
计数值组 - c - 双哈希使用
- graphics - 计算最大反射(朗伯)
- c++ - 使 QGraphicsEllipseItem 始终位于所有图形之上
- asp.net-core - 如何使我的身份服务器会话与外部身份提供者保持同步?
- javascript - 如何检查当前用户是否填写了文档?
- javascript - 是什么让“数组”与 Javascript 中的“对象”不同?【具体原因】
- flutter - 对齐或定位 Flutter 小部件的问题
- javascript - 地理位置不更新状态reactjs
- javascript - 如何在 ajax 响应上添加加载..