2

假设我有一个现有的对象obj1 = {a: 'A', b: 'B', c: 'C', d: 'D'}

我有另一个对象,例如obj2 = {b: 'B2', c: 'C2', d: 'D2', e: 'E2'}

现在我想有选择地将只有几个属性的值从 复制obj2obj1,对于 中的其余属性obj1,我希望它们保持不变。

现在,假设我想复制 propertiesbd. 我想做类似的事情

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's 类型的属性的某种方法
4

1 回答 1

2

我的建议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;
}

该函数在目标对象的类型以及您要从源复制到目标对象的键名的类型方面是通用的。我假设您想要要求从源复制的属性应该与目标中已经存在的属性的类型相同;例如,您不会将属性从 type 的源复制到 type的目标,因为您将 a 写入a并违反目标的类型。TK"a"{a: number}{a: string}numberstring

请注意,我们不需要源对象是精确的TT它只需要完全同意K. 也就是说,我们只说它必须Pick<T, K>使用实用程序类型Pick<T, K>

请注意,我们肯定传入了一个键字符串数组,编译器不仅会推断为string,而且特别是作为字符串字面量类型的联合,它们是(使用类型运算符获取已知键的联合)的集) 。因此,如果您传入,编译器将推断它是类型,而不仅仅是. 这将为您提供您正在寻找的类型安全性,而无需担心JavaScript 中不存在的东西(并且在 TypeScript 中不存在之前,请参阅microsoft/TypeScript#1579)。keyof TkeyofT["b", "d"]Array<"b" | "d">Array<string>nameof


好的,让我们尝试一下:

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 代码链接

于 2021-11-16T19:53:57.917 回答