【问题标题】:How can I merge object values with Lodash (array.push method)?如何将对象值与 Lodash(array.push 方法)合并?
【发布时间】:2021-03-08 20:39:28
【问题描述】:

我有以下两个有点不同的对象的情况:

const primary: PrimaryInterface = {
  numbers: [1,2,3,4], // <= 5
  other_numbers: [4,5,6,7], // <= 8
  //tons of other array fields to merge
}

const child: ChildInterface = {
  numbers: 5, //but be any number, even non unique
  other_numbers: 8
  //other fields, which have the same element stucture
}

现在我想合并child 值,直到primary 对象,作为primary[key].push(child[key])

听起来很简单,我编写了自己的函数来负责这种合并。但是由于我将我的代码重构为打字稿,所以我在直接通过键访问对象值时遇到了问题(通过对象 [键],这个问题说明了原因:Element implicitly has an 'any' type because expression of type 'string' can't be used to index)。

因此,与其重写我几乎所有的代码,我想,Lodash(或任何其他熟悉的对象/数组操作库)及其方法可能是有用的。我在 Lodash 中找到了 merge 方法,它对我的​​情况有用吗?

【问题讨论】:

  • 我不知道您为什么需要使用 lodash,我假设您的代码类似于 this,可以使用一些 type assertions 进行编译。我不认为编译器可以保证没有字符串索引签名的任何迭代对象键的代码都是类型安全的,因此使用类型断言来消除编译器的警告可能是唯一的方法,除非你愿意使用属性名称的硬编码列表。我提供的代码对您有用吗?
  • @jcalz,是的,不止于此。实际上,您在这种情况下给了我一些不同的观点,因为在我自己的情况下,我正在使用 for (const [key, value] of Object.entries(object))。但说实话,实际情况比这要复杂一些,因为primary 不是纯 JS 对象。这是一个mongoose 文档。 (但我们不要考虑这个)而且,我更愿意将您的评论视为答案,因为它确实非常有用。

标签: javascript typescript lodash


【解决方案1】:

TypeScript 中的对象类型是开放,如果你有一个类型的值(比如){a: string, b: number},而你可以确定在 key 处会有一个string 属性"a" 和键 "b" 处的 number 属性,您不能确定除了 "a""b 之外的键处不会有属性。这种开放性允许事物像 interfaceclass 扩展,其中子接口和子类可以拥有比它们的父接口和类更多的属性,而不会违反父接口或类类型的约定。TypeScript 没有“精确类型”(如在microsoft/TypeScript#12936 中请求),您可以说Exact&lt;{a: string, b: number}&gt; 类似于{a: string, b: number},但已知除了"a""b" 之外没有其他属性。因此,除非您在类型中添加string index signature,否则编译器将拒绝建议将对象的键限制为它知道的键。请参阅Why doesn't Object.keys return a keyof type in TypeScript? 以获取可能官方答案在这里。执行此操作的安全方法是遍历已知键的硬编码数组,例如 ["numbers", "other_numbers", /* etc */]


但是,如果您已经有 JavaScript 代码在使用 Object.keys()Object.entries() 之类的对象属性进行迭代,并且它对您有效,那么您已经在 J​​avaScript 中冒了这种风险,没有明显的危害。如果你想继续这样做而不是重写你的代码,那么你总是可以使用type assertions 告诉编译器,虽然你很欣赏它对代码安全的关注,但你确信它对你的代码来说足够安全。目的。例如:

function merge<T>(primary: { [K in keyof T]: Array<T[K]> }, child: T) {

  // the following type assertion is not always safe,
  // since primary may in fact have keys not known to the compiler
  // but we will assume that it doesn't
  const childKeys = Object.keys(child) as Array<keyof T>;

  childKeys.forEach(<K extends keyof T>(k: K) => primary[k].push(child[k]))

}

merge() 的这个实现是一个 generic 函数,它按照您描述的方式工作。 childKeys 的类型断言是您告诉编译器您知道Object.keys(child)string 的数组,但您会将其视为keyof T 的数组,并面临任何可能出现的错误后果那个。

你也可以这样做:

function merge2<T>(primary: { [K in keyof T]: Array<T[K]> }, child: T) {
  for (const k in child) {
    primary[k].push(child[k]);
  }
}

这里编译器实际上做出了相同的(有时是不合理的)假设,您可能会认为for (const k in child) 将仅迭代在类型T 中找到的键。所以你根本不需要任何类型断言。


这个答案的重要部分是类型断言没问题。编译器不能总是知道你对代码的了解。有时编译器是错误的,类型断言有助于解决这个问题。有时它在技术上是正确的,但它引发的问题不太可能出现在您的代码中,而不是重写您的代码,可以使用类型断言来平息抱怨。在任何一种情况下,有时您都希望编译器专注于代码的其他部分,而不是警告实际上没问题的东西。在这种情况下,您坐下来认真思考您正在做什么以确保断言是有道理的,这是有道理的。如果是这样,那就去吧!

Playground link to code

【讨论】:

    猜你喜欢
    • 2018-09-18
    • 2019-07-04
    • 1970-01-01
    • 2019-12-04
    • 1970-01-01
    • 2016-06-12
    • 2018-08-07
    • 2019-05-07
    • 2015-07-07
    相关资源
    最近更新 更多