Search code examples
javascriptvue.jsclassvuejs3ref

Why ref.value can't access private properties declared with #, but with TypeScript's `private` it can?


I have two classes declared.Goods and User

class Goods {
    #price: number
    #quantity: number

    constructor(price: number, quantity: number) {
       this.#price = price;
       this.#quantity = quantity;
    }

    get totalPrice() {
       return this.#price * this.#quantity;
    }

    totalPriceFunc() {
       return this.#price * this.#quantity;
    }
}

class User {
    private id: number
    private name: string;

    constructor(id: number, name: string) {
       this.id = id;
       this.name = name;
    }

    get info() {
       return `id: ${this.id}, name: ${this.name}`;
    }

    infoFunc() {
       return `id: ${this.id}, name: ${this.name}`;
    }
}

init Goods and User and use vue3 ref;

const g = new Goods(10, 100);
console.log('>>>>>>>g ref before: ', g.totalPrice, g.totalPriceGetFunc());
// >>>>>>>g ref before:  1000 1000

const gRef = ref<Goods>(g);
console.log('>>>>>>>g ref after: ', g.totalPrice, g.totalPriceGetFunc());
// >>>>>>>g ref after:  1000 1000

try {
    console.log('gRef totalPrice: ', gRef.value.totalPrice);
    console.log('gRef totalPriceGetFunc: ', gRef.value.totalPriceGetFunc());
} catch (e) {
    console.error(e);
        // TypeError: Cannot read private member #price from an object whose class did not declare it
        // at get totalPrice (HelloWorld.vue:19:15)
        // at Reflect.get (<anonymous>)
        // at MutableReactiveHandler.get (reactivity.esm-bundler.js:928:25)
        // at setup (HelloWorld.vue:50:46)
        // at callWithErrorHandling (runtime-core.esm-bundler.js:199:19)
        // at setupStatefulComponent (runtime-core.esm-bundler.js:7847:25)
        // at setupComponent (runtime-core.esm-bundler.js:7808:36)
        // at mountComponent (runtime-core.esm-bundler.js:5161:7)
        // at processComponent (runtime-core.esm-bundler.js:5127:9)
       //  at patch (runtime-core.esm-bundler.js:4645:11)
}
-------------------------------------------------------------------

const u = ref<User>(new User(1, 'Daniel'));

console.log('u info: ', u.value.info);
// u info:  id: 1, name: Daniel

console.log('u infoFunc: ', u.value.infoFunc());
// u infoFunc:  id: 1, name: Daniel

Why is it that the #price property of Goods cannot be used when using ref to declare responsive state, but private id private name can be used normally?


Solution

  • Primitive values in Vue, such as #price are not made "reactive" as the ref only tracks public properties. This is just the nature of Vue's framework.

    It works when you use "private", as it is coming from TypeScript, not Vue. It is a so-called compile time annotation and therefore will not be enforced at runtime. See more here.