这个事实并不会导致错误:
const thing: Thing = myThing
readonly
存在已知问题,目前按照 这条评论 所述的工作原理实现,其中讲到:microsoft/TypeScript#6532:
为确保向后兼容性,readonly
修饰符不影响包含类型的子类型和可赋值类型关系(但当然会影响单个属性的赋值)。
强制实施 readonly
是一个过于破坏性的更改,因为太多现有的 TypeScript 代码使用的是没有提及 readonly
的类型定义,无法确定如何处理该现有代码,是可变的还是只读的。又由于没有 mutable
关键字,编译器无法区分旧属性是否是只读的与新属性是否是可变的:
interface Foo {
bar: string; // <-- is this a legacy readonly property or a new mutable one?
}
编译器因此将所有非
readonly
注释的属性视为“可能是只读的”,并允许它们之间进行赋值。这是一种妥协。
有一个开放建议在
microsoft/TypeScript#13347,旨在引入编译器标志或其他机制更严格地执行
readonly
,但它已经存在很长时间了,不清楚是否会发生。
另一方面,以下
确实会导致错误:
const arr: Thing[] = myArr
实际上与readonly
属性无关,尽管错误信息(在我看来有些误导性):
// The type 'readonly Thing[]' is 'readonly' and cannot be assigned to the mutable type 'Thing[]'
原因是readonly Thing[]
被解释为readonly
数组语法中的ReadonlyArray<Thing>
,同时Readonly<T>
也是如此,因为带有数组属性的readonly
映射类型会变成ReadonlyArray
属性。
确实,ReadonlyArray<T>
的索引签名是readonly
的,而Array<T>
则不是。但重要的区别在于ReadonlyArray<T>
的类型定义缺少已知会修改数组的Array<T>
方法,例如push()
:
myArr.push({ name: "" }); // error! push does not exist on readonly Thing[];
arr.push({ name: "" }); // okay
由于ReadonlyArray<T>
的类型定义缺少一些在Array<T>
中必需的方法,因此无法将其分配给Array<T>
。它们只是不兼容。就是这样。
以前当您尝试进行这样的赋值时,会出现一个错误,实际上解释了这一点;但那个错误信息相当令人生畏。有关更多信息,请参见microsoft/TypeScript#30839。旧的错误信息大致如下:
因此,在 microsoft/TypeScript#30016 中对其进行了更改,以显示上述提到 readonly
的错误消息。
好的,希望这解释了这两种情况之间的区别。祝你好运!
代码的 Playground 链接