无法分配给 TypeScript 中对象的只读属性
有人能向我解释一下为什么以及如何发生这种情况吗?
我有一个带有 Zustand 状态管理的 TypeScript 应用程序。
在应用程序内部的某个地方,我正在通过从状态中提取某些元素并通过简单的
Object.Assign
克隆它们来更新它们:
let elemToUpdate = Object.assign({},story?.content?.elementsData[nodeId]);
if (elemToUpdate) {
if (elemToUpdate.title) elemToUpdate.title[editorLang] = newName;
else elemToUpdate.title = {[editorLang]:newName} as TextDictionary;
updateElement(nodeId,elemToUpdate);
}
现在有趣的部分是:在我第一次尝试时,更新顺利完成,但我尝试更新的下一个对象失败并显示以下消息:
Tree.tsx:39 Uncaught TypeError: Cannotassign to read only property 'en' of object '#<Object>'
我不明白为什么第一个通过了,但第二个被阻止了。
我知道如何解决这个问题:我需要做一个深度克隆。我只是想了解为什么。
首先
,让我们从为什么代码中的某些对象是
只读
开始。根据您在问题中描述的内容,您使用了一个 Zustand 状态管理器。此类管理器传统上将您存储的数据包装为只读对象,以防止其手动变异(期望您仅通过内置机制更改状态)以保证数据稳定性。因此,如果
story?.content?.elementsData[nodeId]
是 Zustand 状态对象,则它及其所有嵌套对象都将转换为只读。
其次
,让我们定义哪些对象将被阻止。我在这里看到至少两个对象:
elemToUpdate:{...,title:{[lang]:string} } (即
elemToUpdate
及其
title
)。两者都将转换为只读。
第三
,您使用
Object.assign({}, ...)
创建一个新对象(新引用)并克隆源对象的所有属性。请注意,这仅发生在第一级属性中,它不是深度克隆。因此,由于
title
是对另一个对象的引用,它将按原样克隆,并且在新对象中它仍将导致现有的
{ [lang]: string } 对象。有几种方法可以解决这个问题:
- 深度克隆,正如您所提到的
-
手动克隆
title
属性,例如{..., title: { ... elemToUpdate.title } } 或通过 Object.assign
但我不建议您以这种方式改变对象。很可能您的整个算法总体上存在一些架构问题。
这是预料之中的,因为在第一种情况下,您没有为
title
属性分配新值:您只是在更改
title
属性
内部
的值。在第二种情况下,您将重新分配
title
属性的整个值。
您无法更改
readonly
属性。让我们通过一个简单的示例来理解它:
Javascript: 仅用于与问题无关的示例
const user = { // a const is immutable
name: 'John',
}
user.name = 'Pete'; // This works
const user = {
name: 'John'
}
user = { name: 'Pete'} // This doesn't work
Typescript:
const user: Readonly<{
a: {
name: string
}
}> = {
a:{ name: 'John' }
}
user.a.name = 'Pete'; // This works
user.a = { name: 'John' } // Does not work
您的示例中也发生了同样的情况:Typescript 不会检查深度 Readonly 属性。请查看 此处