我试图使用getOwnPropertyDescriptor方法,但无论是普通属性还是只读属性始终是可写的? 你知道什么方法吗?
答案 0 :(得分:2)
关于打字稿,readonly
有两种:编译时只读和运行时只读。
由于OP中没有提供代码,因此我想根据问题,代码类似于:
class Foo {
readonly a = 1
b = 2
}
我们还可以定义一个辅助函数来检查给定对象的属性的readonly
性质
function isWritable<T extends Object>(obj: T, key: keyof T) {
const desc = Object.getOwnPropertyDescriptor(obj, key) || {}
return Boolean(desc.writable)
}
然后我们可以检查readonly
-ness:
const foo = new Foo
console.log(isWritable(foo, 'a'), isWritable(foo, 'b'))
// true true
正在打印true true
是因为foo.a
和foo.b
在运行时都是可写的,因为打字稿中的readonly
关键字对发出的JavaScript代码没有任何作用。
如果我们检查发出的Foo
代码(针对ES6):
class Foo {
constructor() {
this.a = 1;
this.b = 2;
}
}
a
和b
之间根本没有区别。
但是,如果您在打字稿中为foo.a
分配一个新值,则类型系统会抱怨a
是一个只读属性。这正是readonly
关键字的作用:它仅提供编译时约束,并且永远不会影响运行时行为。
foo.a = 1 // Cannot assign to 'a' because it is a constant or a read-only property.
foo.b = 3 // correct
如果需要运行时只读属性,则可以使用get
语法。
class Zoo {
get a () { return 1 }
b = 2
}
发出的代码将是:
class Zoo {
constructor() {
this.b = 2;
}
get a() { return 1; }
}
由于吸气剂存在于原型而非实例中,因此我们需要对isWritable
函数进行一些修改以覆盖getter
。
function isWritable<T extends Object>(obj: T, key: keyof T) {
const desc =
Object.getOwnPropertyDescriptor(obj, key)
|| Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), key)
|| {}
return Boolean(desc.writable)
}
然后我们再次检查readonly
-ness:
const zoo = new Zoo
console.log(isWritable(zoo, 'a'), isWritable(zoo, 'b'))
// false true
现在zoo.a
在编译时和运行时都是只读的。
zoo.a = 1
// Type check fails (compile time): Cannot assign to 'a' because it is a constant or a read-only property.
// Throws an error (run time): Uncaught TypeError: Cannot set property a of #<Zoo> which has only a getter
zoo.b = 3 //correct
CAVEAT
修改后的isWritable
仍然不完善,因为它仅检查原型链上的一个级别。完整的解决方案应该爬上原型链,直到找到描述符或到达终点为止。