我有以下两个有点不同的对象的情况:
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
方法,它对我有用吗?
答案 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 代码,并且它对您有用,那么您已经在 JavaScript 中运行了这种风险,但并不明显伤害。如果您想继续这样做而不是重写您的代码,那么您可以始终使用 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]);
}
}
中找到的键。所以你根本不需要任何类型断言。
这个答案的重要部分是类型断言是可以的。编译器不能总是知道你对代码的了解。有时编译器是错误的,类型断言可用于解决此问题。有时它在技术上是正确的,但它引发的问题不太可能出现在您的代码中,而不是重写您的代码,可以使用类型断言来平息抱怨。在任何一种情况下,有时您都会希望编译器专注于代码的其他部分,而不是警告一些实际上很好的部分。在这种时候,你应该坐下来认真思考你正在做的事情,以确保一个断言是有道理的。如果是这样,那就去吧!