$ref can only be used as the initializer of a variable declaration.
Vue version
3.2.*
Link to minimal reproduction
Steps to reproduce
**import { ref } from 'vue'
const a = data;
let b = $ref({ ...data });
const test = ()=>{
b = $ref(a);
}
</script>**
How should I reassign to B, if I b = $ref(a), the console says $ref can only be used as the initializer of a variable declaration.
If b = a, the message that type "T" cannot be assigned to type "RefValue<UnwrapRef
What is expected?
**import { ref } from 'vue'
const a = data;
let b = $ref({ ...data });
const test = ()=>{
b = $ref(a);
}
</script>**
What is actually happening?
**import { ref } from 'vue'
const a = data;
let b = $ref({ ...data });
const test = ()=>{
b = $ref(a);
}
</script>**
System Info
No response
Any additional comments?
No response
you can only use the $ref() compiler hint to declare a variable. So yes, the correct way to handle this is to do b = a
. The whole point of $ref() is to be able to do b = a
instead of having to write b.value = a
.
The problem you seem to be having is with the types. However your reproduction does not show that. Please update your reproduction to demonstrate that problem.
It sounds as if you have a problem with a generic variable, which can happen when using refs. To give a more helpful answer I need to evaluate the actual code that creates the problem in a runnable reproduction (you might need to use stackblitz to get type evaluation, or provide a github repo.
export const useHooks = <T>(data: T) => {
const initData = data;
let data = $ref(initData );
const reset = ()=>{
data = initData
}
return $$({
data
});
};
Cannot assign type 'T' to type 'RefValue<UnwrapRef
@LinusBorg Looking forward to your help.
This is a more general caveat with generics and refs, and not directly related to the $ref() compiler hint. We need to cover this in the docs. Hold on tight, it gets complicated.
You also used the identifier data
for both the function parameter and a local variable, so here's a cleaned up version, still having the same problem:
export const useHooks = <T>(input: T) => {
const initData = input;
let data = $ref(initData );
const reset = ()=>{
data = initData
}
return $$({
data
});
};
and here's the same code, using a normal ref, having the same problem:
import { ref } from 'vue'
export const useHooks = <T>(input: T) => {
const initData = input;
let data = ref(initData );
const reset = ()=>{
data.value = initData
}
return {
data
};
};
Here's the problem - say we call useHook with an object of the following shape:
useHook({
name: ref('tom')
})
In this case, T
would be equivalent to:
interface T {
name: Ref<string>
}
but once we pass this to ref()
, the value of that ref becomes unwrapped - the value of the ref is now of this shape:
const data = ref(input)
// type of `data.value is now equivalent to UnwrapRef<input>, which means`:
interface X {
name: string // no more Ref<string>
}
so if you do this: data.value = input
again, now you try to assign an object of the first type to a ref with the second type, and those types are not compatible because one property expects a string and the other a Ref<string>
.
This all works at runtime in JS, but there is not clean way to express this in JS without casting, unfortunately. For a normal ref, you can just do this:
const data = ref(input) as Ref<T>
but it seems we do indeed have a challenge when we do our $ref compiler hint. This seems to work at first:
let data = $ref(input) as T
but this messes up the inferred return type of the $$()
as it now does not. We can't just do this either:
let data = $ref(initData) as RefValue<T>
...because RefValue<> adds a marker property type to T, which is not present on the original T.
So for now, you would need to cast on each assignment, which kinda sucks:
data = input as RefValue<T>
The best strategy to not have this problem in the first place is to use tighter generic types that can't contain refs, but I realize that's not always possible or desirable.
We should try and investigate better ways to handle this.
I don't think it's a ref
useHook({
name:'tom',age:18
})
Now T type would be
{name:string;age:number}
const initData = input;
let data = $ref(initData );
data = {
name:'tom',age:18
}
This is a failure,"T" cannot be assigned to type "RefValue
@LinusBorg