Skip to content

Using generic for ref() causes error when reassigning #6766

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ycmjason opened this issue Sep 28, 2022 · 4 comments · Fixed by #11442
Closed

Using generic for ref() causes error when reassigning #6766

ycmjason opened this issue Sep 28, 2022 · 4 comments · Fixed by #11442
Labels
has workaround A workaround has been found to avoid the problem scope: types

Comments

@ycmjason
Copy link
Contributor

Vue version

3.2.40

Link to minimal reproduction

https://stackblitz.com/edit/vitejs-vite-rpmhsh?file=src/main.ts

Steps to reproduce

ref(t) where t is of type generic T will cause error when reassigning the ref to a value of T.

What is expected?

expected refOfT.value = t to work

What is actually happening?

image

Got an error

Type 'T' is not assignable to type 'UnwrapRef<T>'.

System Info

No response

Any additional comments?

No response

@yyx990803 yyx990803 added scope: types has workaround A workaround has been found to avoid the problem labels Sep 29, 2022
@yyx990803
Copy link
Member

Unfortunately this is because ref values have to expose the "unwrapped" type, which unwraps nested refs, to ensure value access correctness:

const a = { b: ref(0) }
const c = ref(a)

c.value.b // should be number

c.value = a // same error you saw

To fix this we will need TypeScript to allow specifying different type constraints for getter and setter of the same property. See microsoft/TypeScript#43662

@ycmjason
Copy link
Contributor Author

ycmjason commented Sep 29, 2022

@yyx990803 understood.

Perhaps at least we can deal with generics that extend primitive types?

https://stackblitz.com/edit/vitejs-vite-hmunsf?file=src%2Fmain.ts

E.g.

const f = <T extends string>(someT: T) => {
  const t = ref(someT);
  t.value = someT; // expected no error
};

const g = <T extends number>(someT: T) => {
  const t = ref(someT);
  t.value = someT; // expected no error
};

In these cases, we can be sure that ref(t) would return Ref<T> because I believe primitive type can't be a Ref?

@Anoesj
Copy link

Anoesj commented Jun 2, 2023

Unfortunately this is because ref values have to expose the "unwrapped" type, which unwraps nested refs, to ensure value access correctness:

const a = { b: ref(0) }
const c = ref(a)

c.value.b // should be number

c.value = a // same error you saw

To fix this we will need TypeScript to allow specifying different type constraints for getter and setter of the same property. See microsoft/TypeScript#43662

Now that that PR was merged into TS 5.1, can this get some attention? I think this is a pretty common pattern, which I totally expected to work when Vue 3.3 landed.

@Loongphy
Copy link

What should we do about generics?

function test<T extends Record<string, any>>(someT: T) {
  const t = ref(someT)
  t.value = someT  // errors
};

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
has workaround A workaround has been found to avoid the problem scope: types
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants