【问题标题】:How to copy values of only a subset of object properties to another existing object in TypeScript如何仅将对象属性子集的值复制到 TypeScript 中的另一个现有对象
【发布时间】:2021-12-27 18:30:45
【问题描述】:

假设我有一个现有对象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 类型的属性

【问题讨论】:

  • 您已经在使用打字稿,所以没有理由不使用字符串,并将它们键入例如keyof obj2.
  • 我的意图是不提供属性名称为'b', 'd',因为那样出错的可能性要高得多
  • 您说“我也不想将属性列表作为字符串提供,因为我希望编译时尽可能安全”,但这与事实相反,您可以使用像"b" 这样的字符串文字类型来挑选你想要的键,但obj2.b 只是一些值,没有办法知道它应该进入目的地的b 属性。就好像我想把一本教科书从爱丽丝的储物柜搬到她的教室,所以我只是把书递给你,根本不说“爱丽丝”这个名字。你怎么知道它去哪儿了?如果我说“爱丽丝”让你拿到书就好了。
  • 你可以使用this,例如,它工作得很好。我什至不知道您将如何开始使用[obj2.b, obj2.d] 来实现它。如果你想要这个作为答案,我可以写出来,只要你明白字符串键确实是唯一合理的解决方案并且它绝对是类型安全的。
  • @jcalz I don't even know how you'd begin to implement it with [obj2.b, obj2.d] 在 C# 中这是一个语言特性,对于 TS 有一个插件:github.com/dsherret/ts-nameof[nameof(obj2.b), nameof(obj2.d)] 将在编译时转换为 ["b", "d"]。优点:重构这些属性时,IDE 不会丢失字符串。缺点:它很冗长,你需要一个插件。

标签: javascript typescript object ecmascript-6 ecmascript-2020


【解决方案1】:

我对@9​​87654328@ 的建议是这样的:

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 类型中的generic,以及要从源复制到目标对象的键名类型K。我假设您希望要求从源中复制的属性应该与目标中已有的属性类型相同;例如,您不会将"a" 属性从{a: number} 类型的源复制到{a: string} 类型的目标,因为您将number 写入string 并违反了目的地。

请注意,我们不需要源对象完全是T;它只需要在K 中的所有键上都与T 一致。也就是我们只说必须是Pick&lt;T, K&gt;使用the Pick&lt;T, K&gt; utility type

请注意,我们肯定会传入一个键字符串数组,编译器不仅会推断为string,而且会特别推断为字符串literal typesunion,它是keyof T 的子集(使用the keyof type operator 得到T 的已知键的联合)。所以如果你传入["b", "d"],编译器会推断它是Array&lt;"b" | "d"&gt; 类型,而不仅仅是Array&lt;string&gt;。这将为您提供所需的类型安全性,而无需担心 JavaScript 中不存在的 nameof 之类的东西(并且在 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"]
)

Playground link to code

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-12-15
    • 2017-05-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多