如何将对象值与 Lodash(array.push 方法)合并?

时间:2021-03-08 20:39:28

标签: javascript typescript lodash

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

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])

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

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

1 个答案:

答案 0 :(得分:1)

TypeScript 中的对象类型是开放,因为如果你有一个类型(比如){a: string, b: number} 的值,而你可以肯定会有一个 {{1} } 键 string 处的属性和 "a"number 处的属性,您不能确定除了 "b" 和 {{ 1}}"。这种开放性允许 "a""b 扩展,其中子接口和子类可以拥有比其父接口和类更多的属性,而不会违反合同父接口或类类型的类型。TypeScript 没有“精确类型”(如 microsoft/TypeScript#12936 中所要求的),您可以在其中说 interface 类似于 class 但已知没有其他类型属性但 Exact<{a: string, b: number}>{a: string, b: number}。因此,除非您在类型中添加 string index signature,否则编译器将拒绝接受对象的键仅限于它知道的键的建议. 请参阅 Why doesn't Object.keys return a keyof type in TypeScript? 以获取此处可能的规范答案。安全的方法是迭代已知密钥的硬编码数组s,例如 "a"


但是,如果您已经拥有使用 "b"["numbers", "other_numbers", /* etc */] 之类的东西迭代对象属性的 JavaScript 代码,并且它对您有用,那么您已经在 J​​avaScript 中运行了这种风险,但并不明显伤害。如果您想继续这样做而不是重写您的代码,那么您可以始终使用 type assertions 告诉编译器,虽然您感谢它对代码安全性的关注,但您确信它足够安全为您的目的。例如:

Object.keys()

Object.entries() 的这种实现是一个 generic 函数,它按照您所描述的方式工作。 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() 是一个 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]); } } 中找到的键。所以你根本不需要任何类型断言。


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

Playground link to code