Subscribe on changes!

Typings error for an reactive object when a property is an Interface that has a private member

avatar
Nov 4th 2020

Version

3.0.2

Reproduction link

https://codesandbox.io/s/focused-ives-64y66?file=/src/useWallet.ts

Steps to reproduce

If you check the Codesandbox example, file useWallet hover on the line 33 you will see the issue that happens.

This doesn't happen on a barebone typescript project (as shown here Typescript Example)

What is expected?

The person property to have the Person type.

What is actually happening?

The person property has an "objectfied" Person type.


This happens only when there are private properties to the interface.

avatar
Nov 5th 2020

The problem is located here:

https://github.com/vuejs/vue-next/blob/a15ec11d03e6a41a975bf2daa72c87f01bd5bd32/packages/reactivity/src/ref.ts#L251

The index signature makes TS loose the Person type and only contains public properties.

Plain TS example:

TS Playground Repro

This will be one for the TS experts (so not me)


workaround

reactive(state) as State
avatar
Nov 5th 2020

Update:

This solves the immediate error but dts tests currently fail on unrelated code for me locally so I can't completely verify:

This breaks unwrapping.

type UnwrappedObject<T> = T & { [P in keyof T]: UnwrapRef<T[P]> } & SymbolExtract<T> 
avatar
Nov 5th 2020

So this will probably have to stay a caveat that requires typecasting. We have to loose the T type in order to have the Unwrapping of possibly nested Refs working.

In this specific scenario it means that the private field is "lost" and the class's type has to be cast manually.

avatar
Nov 5th 2020

when you convert an object to reactive vue will unwrap all possible ref properties, to do that we create a new type which should be identical to the original one (if no property was unwrapped). Usually typescript is able to automatically infer the correct type after unwrapping, but because you have private name, which is technically a property that you don't have access outside of your class, vue will not infer it.

But if you check the intellisense you will see the types provide the same method but typescript cannot infer the correct type, this again is caused by the private name, for typescript to archive the private field it basically hide it in a symbol property:

class Person {
  private name: string
}
// should have the equivalent type
const person = ()=> {
 [Symbol('name')]: string
}

As for the new type creation vue needs to unwrap because of this:

class PersonOther  {
 name: Ref<number>
}

const a = reactive(new PersonOther)

a.name // is a string
avatar
May 25th 2021

Will be fixed in #3791. Duplicate of #2981. May close this one? @HcySunYang

avatar
May 25th 2021

Duplicate of #2981