如何在TypeScript中将一个对象的部分属性值复制到另一个现有的对象中?

4
假设我有一个现有的对象obj1 = {a:'A',b:'B',c:'C',d:'D'}
我有另一个对象,例如obj2 = {b:'B2',c:'C2',d:'D2',e:'E2'}
现在,我只想有选择地复制obj2中只有少数属性的值到obj1,对于obj1中其余的属性,我想让它们保持不变。
现在,假设我想复制属性bd的值。我希望执行以下操作:
obj1 = {a: 'A', b: 'B', c: 'C', d: 'D'};
obj2 = {b: 'B2', c: 'C2', d: 'D2', e: 'E2'};

copyPropertyvalues(
    obj2, // source
    obj1, // destination
    [obj2.b, obj2.d] // properties to copy >> see clarification below
); // should result in obj1 = {a: 'A', b: 'B2', c: 'C', d: 'D2'}

如何编写此方法?请注意,我也不想将属性列表提供为字符串,因为我希望尽可能在编译时进行安全检查。
因此,基本要求如下:
1. 从对象中仅复制几个属性值 2. 复制到现有对象中,而不是创建一个新对象 3. 保留目标对象的其他属性值 4. 避免使用属性名称字符串(例如 'b','d'),并尽可能利用TypeScript类型安全性
注:当我在(伪)代码示例中提到 obj2.b 等内容时: 我不是指字面上的 obj2.b。 而是某种检查编译时检查 b 是否实际上是 obj2 类型的属性的方式。

你已经在使用 TypeScript,所以没有理由不使用字符串,并将它们类型化为例如 keyof obj2 - ASDFGerte
我的意图在这里是不提供属性名称为“'b','d'”,因为那样会有更高的错误几率。 - Arghya C
2
你说“我也不想将属性列表提供为字符串,因为我希望尽可能在编译时获得安全性”,但事实恰恰相反,你可以使用字符串字面类型,如“b”来挑选所需的键,但是obj2.b只是一些值,没有办法知道它应该进入目标的b属性。这就像我想把一本教科书从爱丽丝的储物柜移到她的教室,所以我只是把书递给你,根本没有提到“爱丽丝”的名字。你怎么知道它应该放在哪里?如果我说“爱丽丝”,让你去拿书,那就好多了。 - jcalz
2
你可以像这样做,它完全正常工作。但是,我甚至不知道如何使用[obj2.b, obj2.d]来实现它。如果你想要这个答案,我可以写出来,只要你明白字符串键确实是唯一合理的解决方案,并且它绝对是类型安全的。 - jcalz
1
@jcalz “我甚至不知道你该如何使用[obj2.b,obj2.d]来实现它。” 在C#中,这是一种语言特性,对于TS,有一个插件:https://github.com/dsherret/ts-nameof。 [nameof(obj2.b),nameof(obj2.d)]将在编译时转换为["b","d"]。 优点:当重构这些属性时,IDE不会错过字符串。 缺点:它很冗长,需要一个插件。 - Thomas
显示剩余4条评论
1个回答

3
我对于copyPropertyvalues()的建议是这样的:

function copyPropertyvalues<T, K extends keyof T>(s: Pick<T, K>, d: T, ks: K[]) {
    ks.forEach(k => d[k] = s[k]);
    return d;
}

该函数针对目标对象的类型 T 中的 通用 功能,并且源对象中要复制到目标对象的键名的类型为 K。我假设您希望要求从源对象复制的属性与目标对象中已有的属性具有相同的类型;例如,您不会将来自类型为 {a: number} 的源的 "a" 属性复制到类型为 {a: string} 的目标中,因为这样会将一个 number 写入到一个 string 中,从而违反了目标的类型。
请注意,我们不需要源对象与 T 完全相同;它只需要在 K 中的所有键上与 T 保持一致即可。也就是说,我们只需使用 Pick<T, K> 实用程序类型 来表示源对象必须是 Pick<T, K>
请注意,我们肯定会传递一个键字符串数组,编译器不仅会将其推断为字符串,而且特别是字符串文字类型联合类型,这些类型是T已知键的keyof类型运算符的子集的联合。因此,如果您传递["b", "d"],编译器将推断它的类型为Array<"b" | "d">而不仅仅是Array<string>。这将为您提供所需的类型安全性,而无需担心像nameof这样的东西,在JavaScript中不存在(在TypeScript中也不存在,直到有了它,请参见microsoft/TypeScript#1579)。
好的,让我们试一下:
copyPropertyvalues(
    obj2, // source
    obj1, // destination
    ["b", "d"] // properties to copy
);
console.log(obj1)
/* {
  "a": "A",
  "b": "B2",
  "c": "C",
  "d": "D2"
}  */

看起来它的行为是你想要的。如果你放入错误的键,编译时会报错:
copyPropertyvalues(
    obj2, // error!
    obj1,
    ["a"]
)

copyPropertyvalues(
    obj2,// error!
    obj1,
    ["z"]
)

游乐场代码链接


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接