Typescript Readonly类型不一致

8
我遇到了一个与Typescript Readonly不一致的问题,但我不理解原因。 Typescript v3.9.2 演示: 此处
interface Thing {
  name: string;
}

interface Obj {
  arr: Thing[];
  thing: Thing;
}

const myArr : Readonly<Thing[]> = [{name: 'a'}, {name: 'b'}];
const myThing : Readonly<Thing> = {name: 'foo'};

const myObj: Obj = {
  thing: myThing,
  arr: myArr, // This throws a compile error
}

上述代码给了我以下编译错误:
The type 'readonly Thing[]' is 'readonly' and cannot be assigned to the mutable type 'Thing[]'.(4104)

这个错误是有意义的,我正在给一个可变数组类型赋值只读数组。

我不理解的是为什么对于myObjthing 属性我没有得到类似的错误。

我会期望将 Readonly<Thing> 赋值给一个期望 <Thing> 的属性也是不合法的?

谢谢大家!


我编写了一个 ESLint 规则来防止这种情况:https://github.com/danielnixon/eslint-plugin-total-functions#total-functionsno-unsafe-assignment - danielnixon
1个回答

15

这个事实并不会导致错误:

const thing: Thing = myThing; // no error

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; // error!

实际上与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。旧的错误信息大致如下:

// Type 'readonly Thing[]' is missing the following properties from type 'Thing[]`:
//   pop, push, reverse, shift, and 6 more.

因此,在 microsoft/TypeScript#30016 中对其进行了更改,以显示上述提到 readonly 的错误消息。


好的,希望这解释了这两种情况之间的区别。祝你好运!

代码的 Playground 链接


3
这是一个非常出色和非常详尽的答案,非常感谢!所有上下文链接都特别有帮助,很酷看到语言随着时间的推移而发展 :) - vanchagreen
1
快速记录,readonly 拉取请求的评论链接是错误的。我认为应该是这个 - vanchagreen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接