即使存在关于readonly关键字(#8496(comment))的用途的相当完整的问题,也会创建此问题,我将引用@RyanCavanaugh:
基本上,我们将属性视为“可能只读”或“绝对只读”,并允许从绝对只读的对象到可能只读的对象进行赋值。
我完全同意我们必须防止来自库和/或节点模块的代码,因为今天仍然有模块不包括d.ts文件,而且DefinitelyTyped repo没有100%更新的d.ts文件。
但是,由于主要目的是做一个妥协,让我们在妥协之上添加一个妥协,请参见下面的代码,以了解为什么我们应该让用户在派生类时对readonly
属性的变异更加小心。
**类型脚本版本:**3.3.0-设备201 xxxxx
**搜索词:**最终只读属性派生继承
代码
abstract class Pet {
protected abstract race: string;
protected abstract sayMyRace(): void;
}
class Dog extends Pet {
protected readonly race: string = "Dog";
sayMyRace() {
console.log(this.race);
}
}
class NotADog extends Dog {
protected readonly race: string = "Robot";
}
const scooby = new Dog();
scooby.sayMyRace();
const marvin = new NotADog();
marvin.sayMyRace();
预期行为:
错误的说法是NotADog
中的race不能修改,因为它已经在类Dog
中声明了,我们在两种情况下都设置了相同的属性,这意味着我们确实想覆盖一个常量。
实际行为:
属性,忽略现有父级属性中的只读属性。
Playground链接:
此处
请注意,在文件中,当尝试变更类别中建立的属性时,范例会显示错误。
我并不是要求在每一行代码中的每个关键字上都设置一个检查规则,而是要接受这样一个事实:如果一个类具有只读属性,则任何人都不能从外部改变它。我不是在谈论帮助做出妥协的接口和/或声明文件,而是Typescript documentation声明:
要记住是使用readonly还是const,最简单的方法是询问你是在变量还是属性上使用它。变量使用const,而属性使用readonly。
如果一个只读属性是一个常量变量,那么我们必须保持一致,防止从一个类属性到另一个类属性的任何变化,至少这将允许在代码中添加一个安全检查。
此外,有关接口中的只读属性的文档还指出
某些属性应该只有在第一次创建对象时才能修改。
如果团队认为显式类型的readonly
属性从类到子类之间的变异是正常的,因为妥协已经完成,并且更改规则将破坏声明文件(即使已经过去两年)
所以总结一下:
我将此问题作为一个bug打开,因为文档中声明只读属性是一个常量变量,并且根据有关它的少量信息(仍来自文档),这不是预期的行为。
我还考虑了前面的讨论和为防止任何阻塞向后兼容性而做出的妥协,并要求在默认情况下只在类之间使用readonly检查规则,并在以后的tsconfig规则中允许更多。
5条答案
按热度按时间3b6akqbq1#
现在有什么问题吗
kiayqfof2#
这取决于你是否想防止某人仅仅通过派生其主类来重写任何东西。这不是一个崩溃错误,所以它不是一个致命的警告,但是人们应该知道在TS中没有什么可以避免重写常量属性。
在JS的方式中,我们可以使用Object.freeze()的“方式来做”来防止这个问题。只是要求在类中声明只读属性时有这个冻结方法的语法糖版本,并在派生类和试图变异上述属性时防止任何修改。
编辑:由于冻结一个对象将防止它被删除,使用Object.defineProperty()可能是另一种方法,以避免在运行时和编译时对属性的任何变化。
n8ghc7c13#
让我们使用Object.defineProperty()方法
xvw2m8pv4#
好吧,这里有一个装饰器,不幸的是,它比Typescript执行更好的检查:
如果您启动此程序,则会在尝试重新定义属性时收到错误。
如果你从属性中移除装饰器,编译器仍然会说是,但是在运行时你可以从构造函数中重新定义readonly属性,这种情况是不允许的。
装饰器锁定任何进一步的变异,但允许您
因为没有突变,只有初始化。
现在,我们只需要检查器在尝试改变值时知道它是否是只读的(isReadonly)。
7d7tgy0s5#
有趣的是,与上面的例子不同,这是行不通的:
但是,就像上面的例子所显示的,有一个解决办法,那就是在派生类中重新定义
readonly
属性。