我们可以检查属性是否为只读

时间:2019-02-16 15:51:45

标签: typescript

我试图使用getOwnPropertyDescriptor方法,但无论是普通属性还是只读属性始终是可写的? 你知道什么方法吗?

1 个答案:

答案 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.afoo.b在运行时都是可写的,因为打字稿中的readonly关键字对发出的JavaScript代码没有任何作用。

如果我们检查发出的Foo代码(针对ES6):

class Foo {
    constructor() {
        this.a = 1;
        this.b = 2;
    }
}

ab之间根本没有区别。

但是,如果您在打字稿中为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

Try online

CAVEAT

修改后的isWritable仍然不完善,因为它仅检查原型链上的一个级别。完整的解决方案应该爬上原型链,直到找到描述符或到达终点为止。