Typescript: incorrect typings for unwrapped refs
Version
3.0.6
Reproduction link
https://github.com/shameleo/vue-ts-bug
Steps to reproduce
npm install
npm run dev
Open index.html
browser and look console output.
What is expected?
If typescript is correct, console.log
would print refs and their values.
What is actually happening?
I see values and undefined
Here essential code:
export default defineComponent({
data: () => ({
arr: [1, 2, 3].map((num) => ({ num: ref(num) })),
}),
mounted() {
this.arr.forEach((el) => {
console.log(el.num);
// Typescript shows it is Ref<number>, but actually it is number
console.log(el.num.value);
});
},
});
It's not a bug.this is intentional in options api.
you will see your expected use setup
:
setup(){
const arr = [1, 2, 3].map((num) => ({ num: ref(num) }))
arr.forEach((el) => {
console.log(el.num);
console.log(el.num.value);
});
}
If it is true than I have a lot of questions:
- Using refs is forbidden in optional API? I didn't see any docs or runtime warnings about it.
- Huge amount of tricks would be not available in optional API. Refs bound to URL params, local storage or many other use cases when some factory function returns ref or computed ref. For example, I noticed the issue, when I tried to make tree node, with
checked
prop as ref, which is updated when children's correspondingchecked
props updated. I can't do it? A HUGE dissapointment. - Some libs will only can be used with composition API (for instance). And it is problem as we can't use data and methods from options API in
setup
, so whole component must be written in composition style. - As a result, options API is second class citizen now, and it's not even matter of choice.
If 1-4 is not true, and it's actually works than why typescript should tell me it doesn't?
PS. More appropriate example of composition API would be
import { defineComponent, ref, reactive } from "vue";
export default defineComponent({
setup() {
const r = reactive({
arr: [1, 2, 3].map((num) => ({ num: ref(num) }))
});
r.arr.forEach(el => { console.log(el.num) });
}
});
It all about unwrapping.
PS2. Example is a bit overcomplicated (array of objects instead of simple ref) because I couldn't reproduce issue with just ref. I can now, and it's confusing. Don't know if either different IDE or different packages or whatever made the difference.
It should be
data: () => ({
arr: [1, 2, 3].map((num) => ({ num })),
}),
without ref
Please, next time consider using the forum, the Discord server or StackOverflow for questions first. But feel free to come back and open an issue if it turns out to be a bug 🙂
@posva, the questions is not about how to use it right, as I need ref in real case. Some usecases is in previous post. I didn't see in docs that refs can't be used in options API. Runtime does not warn if I do it. It even seems to work. Except correct typescript support. If using refs in data is forbidden, that I would expect core maintainers to explicitly say this, not some people on forum. Because it has significant consequences (look previous post) and makes options API discouraged legacy way of writing components
@posva He has a point. I don't think we have a rule of ref being forbidden in data
. If we can solve this at the type level, we should.
In general, the Options API and the Composition API shouldn't be mixed. When using composition functions that return Refs, one should use setup()
. This was mentioned in other comments but I couldn't find the right one, only this one about mixins.
However, you are both right about the types: it should properly infer it